Neben reinen Konfigurationen gibt es den häufig angewendeten Spezialfall der Instanzkonfiguration. Eine Instanzkonfiguration dient dazu, eine konfigurierbare Komponente zu instanziieren.
Instanzkonfigurationen werden statt von ConfigurationItem von PolymorphicConfiguration abgeleitet. Sie hat ein ausgezeichnetes Konfigurationsproperty class (vgl. PolymorphicConfiguration#getImplementationClass()). Dieses spezielle Property hat zwei Funktionen:
Im folgenden Beispiel deklariert eine abstrakte konfigurierbare Komponente Component ein Konfigurationsinterface Component.Config, und referenziert dieses in ihrem Konfigurationskonstruktor. Für den Komponentennamen und den Namen des Konfigurationsinterfaces gibt es keine Einschränkungen.
public abstract class Component {
public interface Config<I extends Component> extends PolymorphicConfiguration<I> {
int getOption();
}
/** Konfigurationskonstruktor */
Component(InstantiationContext context, Config<?> config) {
...
}
}
Der Typparameter von PolymorphicConfiguration benennt den Typ, der aus der Konfiguration instanziiert wird, und aus der Schranke dieses Parameters wird der Defaultwert des Properties class abgeleitet. Deshalb bindet das Konfigurationsinterface eines Basistyps den Parameter nicht, sondern hält ihn offen und schränkt ihn auf diesen Basistyp ein; erst das Konfigurationsinterface einer konkreten Implementierungsklasse bindet ihn auf diese Klasse. Das Binden des Parameters ist die Art, in der eine Konfiguration ihre Implementierungsklasse angibt; ein abgeleitetes Konfigurationsinterface deklariert das Property class deshalb nicht erneut.
Bindet ein Basisinterface den Typparameter dagegen selbst, bleibt einer Spezialisierung nichts mehr zu binden. Jede Spezialisierung muss das Property class dann erneut deklarieren und mit @ClassDefault annotieren — andernfalls ist bei der Verwendung über den Tag-Namen nur der Konfigurationstyp bekannt, nicht aber die Implementierungsklasse. Genau diese Wiederholungen entfallen, wenn der Parameter offen bleibt.
Die konkrete Ableitung MyComponent1 verwendet dieselben (geerbten) Konfigurationsoptionen (liefert lediglich einen Implementierungsanteil):
public class MyComponent1 extends Component {
MyComponent1(InstantiationContext context, Config<?> config) {
...
}
}
Eine Instanz von MyComponent1 kann durch Angabe der Implementierungsklasse und ihrer Konfigurationoptionen konfiguriert werden:
<component class="my.package.MyComponent1"
option="42"
/>
MyComponent2 deklariert ein spezialisiertes Konfigurationsinterface und erlaubt damit neben der Konfigurationsoption option aus Component die zusätzliche Option other-option:
public class MyComponent2 extends Component {
public interface Config extends Component.Config<MyComponent2> {
String getOtherOption();
}
MyComponent2(InstantiationContext context, Config config) {
...
}
}
In der Konfiguration einer Instanz von MyComponent2 ist damit implizit die Angabe eines Wertes für die spezialisierte Konfigurationsoption other-config möglich:
<component class="my.package.MyComponent2"
option="42"
otherOption="Hello World!"
/>
Die Kombination der Implementierungsklasse MyComponent1 mit der Konfigurationsoption other-option würde als Konfiguationsfehler erkannt werden.
Eine konfigurierbare Komponente leitet von AbstractConfiguredInstance ab und übergibt ihre Konfiguration an den Basiskonstruktor. Sie ist damit ein ConfiguredInstance, und die Konfiguration, aus der eine Instanz gebaut wurde, bleibt an der Instanz lesbar — etwa für den Konfigurationseditor, der sie anzeigt, oder für Code, der eine Variante davon ableitet. Ein Konfigurationskonstruktor, der die Konfiguration entgegennimmt und verwirft, nimmt der Instanz diese Auskunft.