Oft ist es nötig Implementierungen auszutauschen, diese Implementierungen benötigen oft zusätzliche Properties. Um die Austauschbarkeit und das Zuweisen von weiteren Properties zu vereinfachen, ist es möglich Instanzen einer Klasse mit der Angabe eines Properties von MyCoRe erzeugen zu lassen.
Für das Erzeugen einer Instanz kann die statische Methode getInstanceOf
in der Klasse
MCRConfiguration2
mit einem frei wählbaren Konfigurationsnamen aufgerufen werden. Wenn das Vorhandensein einer
Instanz unabdingbar ist, bietet sich die Nutzung der Methode createConfigurationException
an.
|
|
Damit eine entsprechende Instanz erzeugt werden kann, muss ein entsprechendes Property die zu instanziierende Klasse mit ihrem vollqualifizierten Klassennamen benennen.
|
|
Diese Klasse darf nicht abstrakt sein und muss
instance
im Namen als Factory-Methode
anbieten. Falls beides vorhanden ist, wird der Konstruktor bevorzugt.
Endet der gewählte Konfigurationsname (wie in diesem Beispiel) auf .Class
oder .class
,
so werden für die im Folgenden beschriebenen Funktionen alle weitere Properties relativ zu dem davorstehenden
Namensanteil (im Beispiel My.Configurable
) gesucht, andernfalls relativ zum vollen Namen.
Falls die Klasse MyConfigurableClass
zusätzliche Konfigurationswerte benötigt, so können
Felder oder Methoden mit der Annotation @MCRProperty
versehen werden.
Annotierte Felder muss öffentlich und vom Typ String
oder Map<String, String>
sein.
Annotierte Methoden muss öffentlich sein und einen einzigen Parameter vom Typ String
oder Map<String, String>
nehmen.
Die Annotation hat folgende Konfigurationsmöglichkeiten:
mame
gibt den Namen des zugehörigen Properties an, sofern das annotierte Feld bzw. die annotierte Methode den Typ String
nutzt.
Genau dann, wenn das annotierte Feld bzw. die annotierte Methode den Typ Map<String, String>
nutzt, muss hier der besondere Wert *
angegeben werden.
In diesem Fall wird statt einem einzelnen Property eine Map
mit allen unterhalb des gewählten Konfigurationsnamens vorhandenen Properties bereitgestellt.
required
gibt an, ob ein entsprechendes Property vorhanden sein muss. Der Standardwert ist true
.
Wenn false
gewählt wird und kein entsprechendes Property vorhanden ist, wird der (z.B. im Konstruktor gesetzte) vorhandene Wert des annotierten Feldes nicht auf null
gesetzt.
Eine annotierte Methode wird in diesem Fall nicht aufgerufen. Wird eine annotierte Methode aufgerufen, so ist der übergebene Wert niemals null
.
absolute
gibt an, ob der unter name
angegeben Wert absolute (und nicht relativ zum Konfigurationsnamen) aufgefasst werden soll.
defaultName
gibt den absolut aufgefassten Namen eines Standardproperties an, dass verwendet werden soll, wenn das eigentliche Property nicht vorhanden ist.
Dieses Standardproperty muss auf jeden Fall konfiguriert sein.
Dieses Vorgehen ist hart kodierten Standardwerten vorzuziehen.
order
gibt die Reihenfolge an, in der die annotierten Felder gesetzt bzw. die annotierten Methoden aufgerufen werden, wobei niedrigere bevorzugt werden.
Der Standardwert ist 0
. Haben z.B. mehrere annotierte Methoden denselben Wert, so wird keine Reihenfolge garantiert.
Java-Code in der Klasse MyConfigurableClass
(Auszug):
|
|
Eine passende Konfiguration könnte beispielsweise so aussehen:
|
|
In diesem Beispiel bekommt das Feld foo
den Wert custom
aus dem Property MCR.Configurable.Foo
.
Der vordefiniert Wert default
wird überschrieben.
Das Feld map
bekommt eine Map
mit den Einträgen Foo=custom
und Bar=baz
.
Die Methode setNumber
wird mit dem Wert 42
aus dem Standardproperty MCR.InterestingNumber.Global
aufgerufen.
Wäre das Property MCR.InterestingNumber.Local
nicht auskommentiert, so würde die Methode mit dessen Wert 23
aufgerufen werden.
Falls die Klasse MyConfigurableClass
statt einfachen Konfigurationswerten komplexere Objekte benötigt, so können
Felder oder Methoden mit der Annotation @MCRInstance
versehen werden. Annotierte Felder müssen öffentlich und von
einem Typ sein, der zuweisungskompatibel zu dem im Attribut valueClass
der Annotation angegebenen Klasse sein muss.
Annotierte Methoden müssen öffentlich sein und einen einzigen Parameter von einem solchen Typ nehmen.
Die Annotation hat folgende Konfigurationsmöglichkeiten:
mame
gibt den Namen des zugehörigen Properties an.
In diesem muss die zu instanziierende Klasse mit ihrem vollqualifizierten Klassennamen benennen.
Endet der gewählte ursprünglich Konfigurationsname (wie in diesem Beispiel) auf .Class
oder
.class
, so wird diese Endung übernommen.
valueClass
benennt die Klasse, zu der die in name
benannte Klasse
zuweisungskompatibel sein muss. Dass annotierte Feld bzw. der Parameter der annotierten Methode muss ebenfalls diesen Typ haben.
required
gibt an, ob ein entsprechendes Property vorhanden sein muss. Der Standardwert ist true
.
Wenn false
gewählt wird und kein entsprechendes Property vorhanden ist, wird der (z.B. im Konstruktor gesetzte) vorhandene Wert des annotierten Feldes nicht auf null
gesetzt.
Eine annotierte Methode wird in diesem Fall nicht aufgerufen. Wird eine annotierte Methode aufgerufen, so ist der übergebene Wert niemals null
.
order
gibt die Reihenfolge an, in der die annotierten Felder gesetzt bzw. die annotierten Methoden aufgerufen werden, wobei niedrigere bevorzugt werden.
Der Standardwert ist 0
. Haben z.B. mehrere annotierte Methoden denselben Wert, so wird keine Reihenfolge garantiert.
Java-Code in der Klasse MyConfigurableClass
(Auszug):
|
|
Eine passende Konfiguration könnte beispielsweise so aussehen:
|
|
In diesem Beispiel bekommt das Feld foo
den Wert foo
aus dem Property MCR.Configurable.Foo
.
Zudem werden zwei weitere Instanzen der Klasse MyConfigurableClass
erzeugt und den Feldern nested1
und
nested2
zugewiesen. Dem Feld foo
dieser geschachtelten Instanzen wird der Wert bar
bzw. der Wert baz
zugewiesen.
Falls die Klasse MyConfigurableClass
statt mehrere statt einzelnen komplexere Objekte benötigt, so können
Felder oder Methoden mit der Annotation @MCRInstanceMap
oder @MCRInstanceList
versehen werden. Annotierte Felder müssen öffentlich und vom
Typ Map<String, X>
bzw. List<X>
sein, wobei X
zuweisungskompatibel zu dem im Attribut valueClass
der Annotation angegebenen Klasse sein muss. Annotierte Methoden müssen öffentlich sein und einen einzigen Parameter von einem solchen Typ nehmen.
Die Annotationen haben dieselben Konfigurationsmöglichkeiten wie @MCRInstance
. Das Attribute name
ist jedoch optional und gibt nur einen Prefix für die zu beachtenden Properties an. Wird kein Wert angegeben, so werden
alle Properties behandelt.
Alle behandelten Properties werden ausgewertet und zur Erzeugung geschachtelter Instanzen herangezogen.
Der führende Namensanteil (abzüglich dem ggf. im Attribut name
angegebenen Prefix) wird im Falle von @MCRInstanceMap
als Schlüssel für die gebildete Map
und im Falle von @MCRInstanceList
(als Zahlenwert interpretiert) für die
Reihenfolge der gebildeten List
verwendet. Endet der gewählte ursprünglich Konfigurationsname (wie in diesem Beispiel) auf
.Class
oder .class
, so wird diese Endung übernommen.
Java-Code in der Klasse MyConfigurableClass
(Auszug):
|
|
Eine passende Konfiguration könnte beispielsweise so aussehen:
|
|
In diesem Beispiel ist Nested
ein Interface oder eine (ggf. abstrakte) Basisklasse.
Das Feld nestedMap
bekommt als Wert eine Map
mit zwei Einträge mit Schlüsseln
foo
und bar
. Die Werte dieser Einträge sind Instanzen der Klassen
MyNestedClassA
bzw. MyNestedClassB
. Das Feld nestedList
bekommt als
Wert eine List
mit ebenfalls zwei Einträge. Die Werte dieser Einträge sind Instanzen der
Klassen MyNestedClassC
bzw. MyNestedClassD
. Alle vier erzeugte Instanzen wurden
mit weiteren Konfigurationswerten konfiguriert.
Auf das Attribut name
der Annotation verzichtet werden. In diesem Fall entfällt der entsprechende
Namensbestandteil in den Properties. Allerdings kann die Klasse keine weiteren Konfigurationswerte bekommen,
da alle vorhandenen Properties für die Einträge der Map
bzw. List
verwendet werden.
Java-Code in der Klasse MyConfigurableClass
(Auszug):
|
|
Eine passende Konfiguration könnte beispielsweise so aussehen:
|
|
Das Feld nestedMap
bekommt als Wert eine Map
mit zwei Einträge mit Schlüsseln
foo
und bar
. Die Werte dieser Einträge sind Instanzen der Klassen
MyNestedClassA
bzw. MyNestedClassB
.
Da man Felder eines Objektes nicht zuweisen kann bevor der Konstruktor aufgerufen wurde, man jedoch für die
Initialisierung möglicherweise die zugewiesenen Felder benötigt, gibt es die Möglichkeit weitere Methoden
nach der Initialisierung aufrufen zu lassen. Dazu muss die Methode public
sein, entweder keinen
oder genau einen Parameter vom Typ String
nehmen und mit @MCRPostConstruction
annotiert sein. Die Annotation hat ein optionale Attribut order
das analog zum gleichnamigen
Attribut von @MCRProperty
funktioniert.
Java-Code in der Klasse MyConfigurableClass
(Auszug):
|
|
Falls die Methode einen Parameter nimmt, so wird der Konfigurationsnamen übergeben.
Im Beispiel also MCR.Configurable.Class
.
Die Reihenfolge der Initialisierung ist:
@MCRProperty
, @MCRInstance
, etc. annotierten Felder (in aufsteigenden order
-Reihenfolge)@MCRProperty
, @MCRInstance
, etc. annotierten Methoden (in aufsteigenden order
-Reihenfolge)@MCRPostConstruction
annotierten Methode (in aufsteigenden order
-Reihenfolge)
Zuweilen möchte man in der Konfiguration diverse geschachtelte Instanzen vorhalten
(die teilweise aus einer erheblichen Menge von Konfigurationswerten bestehen können),
diese aber nicht in der tatsächlich verwendeten Konfiguration verwenden
(z.B. exemplarische Konfigurationen oder solche, die nur gelegentlich oder alternativ benötigt werden).
Damit in solchen Situationen nicht mit Aus- und Einkommentieren der zugehörigen Konfigurationswerte gearbeitet werden muss,
besteht mit MCRSentinel
eine Möglichkeit, die Instantiierung von geschachtelten Instanzen zu unterbinden.
Dazu kann bei den Annotationen @MCRInstance
, @MCRInstanceMap
und @MCRInstanceList
jeweils
das Attribut sentinel
verwendet werden.
Dies führt dazu, dass bei jeder geschachtelten Instanz zunächst der Konfigurationswert mit dem Namen Enabled
ausgewertet wird.
Ist dieses vorhanden und hat den Wert false
, so wird die Instantiierung der jeweiligen geschachtelten Instanz unterbunden.
Java-Code in der Klasse MyConfigurableClass
(Auszug):
|
|
Eine passende Konfiguration könnte beispielsweise so aussehen:
|
|
In diesem Beispiel würden nur die beiden geschachtelten Instanzen mit den Namen foo
und bar
instanziiert werden.
Die geschachtelte Instanz mit den Namen baz
wird vollständig ignoriert.
Mit dem Attribut name
von MCRSentinel
kann der Name des ausgewerteten Konfigurationswerts angepasst werden.
Mit dem Attribut defaultValue
von MCRSentinel
kann das Standardverhalten bei Nichtvorhandensein des Konfigurationswerts angepasst werden.
In Situationen in denen die oben beschriebene Anforderung (dass eine zu konfigurierende Klasse einen
öffentlichen, parameterlosen Konstruktor oder eine qualifizierte Factory-Methode haben muss) nicht umsetzbar ist
oder eine derartige Umsetzung anderen Design-Entscheidungen (z.B. Kapselung, Immutability) entgegen spricht, oder die Klasse allgemein vom Konfigurationsmechanismus
entkoppelt werden soll, so kann die zu instanziierende Klasse mit MCRConfigurationProxy
annotiert werden. Diese Annotation hat ein Attribut proxyClass
das eine Klasse benennt, die stattdessen
mit dem hier beschriebenen Mechanismus konfiguriert und anschließend verwendet wird, um eine Instanz der
eigentlich zu instanziierende Klasse zu erlangen. Hierzu muss diese Klasse das Interface
Supplier<X>
implementieren, wobei X
die eigentlich zu instanziierende
Klasse ist.
Java-Code in der Klasse MyConfigurableClass
:
|
|