23. März 2012 | 5 min lesezeit

Property-Injection mit CDI

Die Konfiguration von Anwendungen über Dateien mit Key-Value-Paaren (*.properties-Dateien) ist eine regelmäßig wiederkehrende Aufgabe für Java-Entwickler. Häufig wird an einer konkreten Stelle im Code dann nur der Wert eines Konfigurations-Parameters benötigt. Um nicht an jeder dieser Stellen im Code die Properties-Datei neu laden zu müssen, bietet sich an, das Laden der Properties-Datei an eine zentrale Stelle im Code zu verlagern. Hier stellt sich allerdings die Frage, wie auf diese zentrale Stelle zugegriffen werden kann. Um hier die hässliche Variante einer statischen Zugriffsmethode zu vermeiden, bleibt eigentlich nur Dependency-Injection.

Da der aktuelle Java-EE-Standard für Dependency-Injection (CDI) die Injection von Properties noch nicht unterstützt, hat open knowledge diesen Standard um genau dieses Feature erweitert. Im letzten Blog-Eintrag bin ich bereits auf die Möglichkeit der Erweiterung von CDI über sogenannte Extensions eingegangen. An dieser Stelle möchte ich nun vorstellen, wie die Properties-Extension von open knowledge verwendet werden kann, um Properties in einfache Felder von CDI-Beans zu injizieren. Im Beispiel verwende ich eine Klasse Application, die Informationen zur aktuell laufenden Applikation zur Verfügung stellen soll, um diese z.B. in einer Info-Box darzustellen. Da sich diese Informationen häufig ändern können, sollen sie über eine Properties-Datei konfigurierbar sein. Dazu erstellen wir im Ordner src/main/resources/de/openknowledge/blog/property die Datei application.properties mit dem folgenden Inhalt:

Unter Verwendung der open knowledge CDI-Extensions, die man hier herunterladen und mit Maven bauen kann, kann diese Property dann ganz leicht in eine Application-Bean injiziert werden:

Testen von CDI-Injection

Zum Testen bietet das Modul openknowledge-cdi-test einen Testrunner, mit dem JUnit-Tests durchgeführt werden können, in die CDI-Beans injiziert werden können. Dabei sind die Testklassen selber CDI-Beans:

Injection von anderen Datentypen

Properties müssen nicht vom Typ String sein. Alle einfachen Datentypen werden unterstützt:

Und können injiziert werden:

Es können sogar beliebige Objekte injiziert werden, wenn sie einen String-Construktor haben. Dieser wird dann vom Container aufgerufen und bekommt den Wert der Property übergeben:

Die zugehörige Properties-Datei enthielte dann folgenden Eintrag:

Eine Besonderheit ist die Möglichkeit, Objekte vom Typ java.util.Properties injizieren zu lassen. Hierbei kann am Ende des Property-Namens ein * als Wildcard angegeben werden. Das injizierte Properties-Objekt enthält dann alle Property-Values, die mit dem entsprechenden Namen beginnen:

Nun können wir auch den Test noch entsprechend erweitern:

Ablageort der Properties-Datei

Wie wir an dem Test erkennen können, funktioniert unsere Property-Injection bereits. Es stellt sich aber die Frage, aus welcher Quelle die Properties geladen werden. Wenn, wie im Beispiel, keine Quelle angegeben ist, wird eine Standardquelle verwendet. Diese ist davon abhängig, in welcher Klasse sich der Injection-Point befindet. In unserem Fall befindet er sich in der Klasse de.openknowledge.blog.Application, die zugehörige Standardquelle ist dann de/openknowledge/blog/application.properties und genau dort haben wir unsere Properties-Datei auch hingelegt.

Wenn wir die Quelle explizit angeben wollen, können wir dies an der @Property-Annotation tun: @Property(name = "application.name", source = "application.properties") Die Datei wird dann relativ zum aktuellen Package aufgelöst. Würden wir die Properties-Datei also in das Package de.openknowledge.blog.property.resources legen, könnten wir sie mit @Property(..., source = "resources/application.properties") referenzieren. Pfade, die relativ zum default-package liegen, können mit einem führenden / angegeben werden. Im folgenden Beispiel würde die Datei also direkt im default-package liegen: @Property(..., source = "/application.properties") und in diesem @Property(..., source = "/resources/application.properties") läge sie im package resources. Es können auch Properties-Dateien aus dem Dateisystem referenziert werden. Im folgenden Beispiel würde dies relativ zum aktuellen Pfad der Anwendung geschehen: @Property(..., source = "file://resources/application.properties"). Eine absolute Pfad-Angabe ist auch hier mit einem führenden / möglich: @Property(..., source = "file:///resources/application.properties") (man beachte die drei Slashes nach dem file:).

Gemeinsame Nutzung einer Properties-Datei

In unserem Beispiel verwenden wir für jede Property in der Klasse Application dieselbe Datei. Um diese nicht jedes Mal angeben zu müssen, gibt es die Annotation @PropertySource("/application.properties"). Diese kann an die Klasse gesetzt werden, so dass die angegebene Quelle dann für die gesamte Klasse Gültigkeit hat. Falls eine Property in der Klasse aus einer anderen Datei kommen soll, kann die Quelle überschrieben werden:

Mit der Annotation @PropertySource kann auch ein Package annotiert werden (in der jeweiligen package-info.java Datei). Die Quelle gilt dann für das Package und alle Unterpackages. Auch diese kann entsprechend mit einer @PropertySource-Annotation auf Klassen-Ebene bzw. mit der Angabe des source-Attributes an der @Property-Annotation überschrieben werden. Auch in der @PropertySource-Annotation können natürlich sowohl classpath- als auch dateibasierte Pfade angegeben werden.

Pfad-Ersetzung

Das generelle Problem von dateibasierten Pfaden ist immer, dass sie sich von System zu System ändern: Auf dem Entwickler-Rechner ist es immer ein anderer Pfad als auf dem Test-Server. Häufig unterscheiden sich auch die Pfade des Test-Servers und des Produktiv-Servers. Um dieses Problem zu beheben, bieten die open knowledge CDI Extensions die Möglichkeit, den Pfad durch Properties zu setzen. Hierzu können entweder System-Properties verwendet werden oder Properties aus derselben Properties-Datei. Wenn man also die Properties-Datei in ein separates Konfigurationsverzeichnis legen will, kann man das tun und sie über @PropertySource(name = "file://${application.config.dir}/application.properties") referenzieren. Die Property application.config.dir kann man dann beim Start über -D angeben. So kann man z.B. auch für die Tests des Continuous Integration Servers eine andere Datei verwenden als lokal oder auf dem Server.

Das Ersetzen von Properties funktioniert auch innerhalb der Property-Values:

Die zu ersetzenden Properties müssen sich dabei in derselben Properties-Datei befinden oder können als System-Properties (über -D) angegeben werden.

Im letzten Blog habe ich gezeigt, wie einfach sich portierbare CDI-Extensions erstellen lassen. Die hier vorgestellte Extension ist ein gutes Beispiel dafür, wie diese die tägliche Anwendungsentwicklung erleichtern können.


Keine Kommentare

Kontakt

OPEN KNOWLEDGE GmbH

Standort Oldenburg:
Poststraße 1, 26122 Oldenburg

Standort Essen:
II. Hagen 7, 45127 Essen