Spring 3.1 – Was gibt’s Neues?
Spring 3.1 steht vor der Tür – und mit ihm eine Reihe neuer, interessanter Features. Eines dieser Features stellen wir in diesem Artikel vor: die neue Environment Abstraction, die mit ihren Bean Definition Profiles umgebungsspezifische Bean-Definitionen ermöglicht. Im nächsten Teil geht es dann um die Cache Abstraction, die ein allgemeines API für Cache-Funktionalitäten mit Unterstützung verschiedener Implementierungen anbietet.
Teil 1: Environment Abstraction
Jede Anwendung durchläuft von der Entwicklung über die Testphase bis zum produktiven Einsatz unterschiedliche Lebensphasen. In diesen Phasen stehen in der Regel unterschiedliche Ressourcen zur Verfügung, seien es Hard- und Software, Persistenzmedien oder Fremdkomponenten. In der Konfiguration einer Anwendung bedeutet das zumindest unterschiedliche Konfigurationsdaten wie Username/Password bei einer DataSource
. Es können sich in den Umgebungen aber auch Infrastrukturkomponenten unterscheiden. So kann es sein, dass man in einer Entwicklungsumgebung einen DataSourceTransactionManager
verwendet, während in Produktion ein JTA-Transaktionsmanager Anwendung findet.
Umgebungsspezifische Spring-Konfigurationen werden meistens per PropertyPlaceholderConfigurer
oder austauschbaren ApplicationContext
-Dateien realisiert. PerPropertyPlaceholderConfigurer
können einzelne Zeichenketten aus der Spring-Konfiguration in eine Properties-Datei ausgelagert werden. So können beispielsweise Verbindungsdaten für eine Datenbank für unterschiedliche Umgebungen konfigurierbar gehalten werden (Listing 1). Was aber, wenn man in der einen Umgebung die DataSource
direkt erstellen möchte, sie in der anderen Umgebung aber per JNDI aus einem JEE-Container holen möchte? Hier enden die Möglichkeiten des PropertyPlaceholderConfigurers
, da keine Bean-Definitionen ausgetauscht werden können. Mit austauschbaren ApplicationContext
-Dateien ist das Problem lösbar, aber bei einer Zusteuerung per Build-Prozess ist das Anwendungsartefakt auf eine Umgebung spezialisiert. Das import-Tag kann Platzhalter auflösen, die entweder in JVM-Systemproperties oder in Umgebungsvariablen definiert sind:
<import resource="${env}-applicationContext.xml" /> <context:property-placeholder location="classpath:jdbc.properties" /> <bean id="dataSource" class="${jdbc.dataSourceClassName}"> <property name="driverClassName" value="${jdbc.driverClassName}"/> <property name="url" value="${jdbc.url}"/> <property name="password" value="${jdbc.password}"/> <property name="username" value="${jdbc.username}"/> </bean>
Spring 3.1 konsolidiert und erweitert die bisherigen Möglichkeiten des Umgebungsmanagements. Zunächst betrachten wir die neuen Bean Definition Profiles.
Bean Definition Profiles
Wir gehen im Folgenden von einer Anwendung aus, die im Infrastrukturbereich für den Datenzugriff einen Transaktionsmanager und eine DataSource
benötigt. In der Entwicklungsumgebung läuft unsere Anwendung auf einem Tomcat. Eine lokale DataSource
wird erzeugt, ein DataSourceTransactionManager
regelt das Transaktionsverhalten. Als Integrations- und Produktionsumgebung läuft unsere Anwendung auf einem Application Server, daher werden Transaktionsmanager und DataSource
per JNDI aus dem Verzeichnis des Java-EE-Containers geholt.
Natürlich können diese beiden Konfigurationen nicht gleichzeitig geladen werden, allein schon die identischen Bean-IDs schaffen Probleme. Andererseits wollen wir aber ein Anwendungsartefakt, sei es ein jar, ein war oder ein ear, schaffen, das in allen Umgebungen lauffähig ist. Wir brauchen also einen Mechanismus, der es erlaubt, bei Bedarf bestimmte Spring Beans zu aktivieren oder zu deaktivieren.
Genau dafür werden in Spring 3.1 die Bean Definition Profiles eingeführt. Das beans
-Tag, also das Wurzel-Tag einer Spring-XML-Konfiguration, wird um das Attribut profile
erweitert. Ist dieses Attribut gesetzt, werden die innerhalb des Tags definierten Bean-Definitionen nur hinzugefügt, wenn ein Umgebungsprofil mit dem entsprechenden Namen aktiviert ist.
Seit Spring 3.1 ist es auch möglich, beans
-Tags ineinander zu schachteln. So können Spring-Beans verschiedener Umgebungen in einer Datei definiert werden. Die Konfiguration unserer Anwendung sieht nun so aus wie in Listing 2. Dabei haben wir jeweils für die Entwicklungs-, Integrations- und Produktionsumgebung ein Profil erstellt. Die Konfigurationen für Integrations- und Produktionsumgebung sind zwar identisch, trotzdem mag es außerhalb dieser Konfigurationsdatei Unterschiede in der Konfiguration geben.
<beans ...> <beans profile="dev"> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="org.SomeDriverClassName"/> <property name="url" value="someUrl"/> <property name="password" value="somePassword"/> <property name="username" value="someUsername"/> </bean> </beans> <beans profile="int,prod"> <tx:jta-transaction-manager/> <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/> </beans> </beans>
Mit der Annotation @Profile
kann dieses Feature übrigens auch außerhalb von XML-Konfigurationen verwendet werden.
So weit, so gut. Aber wir haben keines der Profile aktiviert, also werden keine Spring Beans erzeugt. Wie aktiviert man also ein Umgebungsprofil?
[ header = Seite 2: Environment ]
Environment
Hier kommt die neue Umgebungsabstraktion ins Spiel. Ab Spring 3.1 bringt jeder ApplicationContext
ein Objekt mit, das das Interface Environment
implementiert. Dieses Objekt muss alle aktiven Profile kennen.
Schauen wir zuerst auf den programmatischen Ansatz (Listing 3). Haben wir den ApplicationContext
direkt zur Hand, so können wir das aktive Profil direkt am Environment
-Objekt setzen. Das Setzen des aktiven Profils muss vor dem Erzeugen der Beans, also vor dem Aufruf der refresh
-Methode, stattfinden.
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext(); applicationContext.getEnvironment().setActiveProfiles("dev"); applicationContext.load("META-INF/applicationContext.xml"); applicationContext.refresh();
In der Regel wollen wir die Entscheidung für ein bestimmtes Profil nicht im Code treffen. Daher ist es möglich, Profile über die Property ’spring.profiles.active‘ zu aktivieren. Diese Property kann kommasepariert Profilnamen der zu aktivierenden Profile enthalten und kann über viele mögliche Quelle eingelesen werden. Hier kommt die zweite Aufgabe des Environments ins Spiel: das Management von PropertySources
.
PropertySources
Eine PropertySource
ist eine Quelle für Properties. Das Environment
beinhaltet eine Hierarchie von PropertySources
. Wenn unsere Anwendung eine Property anfragt (Listing 4), werden diePropertySources
der Reihe nach gefragt, ob sie die Property kennen.
GenericXmlApplicationContext applicationContext = new GenericXmlApplicationContext(); String propertyActiveProfiles = applicationContext.getEnvironment().getProperty("spring.profiles.active");
Das DefaultEnvironment
registriert automatisch PropertySources
für die JVM System Properties und die Systemumgebungsvariablen. Dabei steht die PropertySource
für die JVM System Properties in der Hierarchie weiter oben, da sie in der Regel spezialisierter sind als die Umgebungsvariablen, die ja für alle JVMs auf der Maschine gelten.
Wollen wir also nun das Profil ‚dev‘ aktivieren, so können wir unsere JVM mit dem Parameter -Dspring.profiles.active=dev
starten oder eine Umgebungsvariable mit dem Namen ’spring.profiles.active‘ und dem Wert ‚dev‘ erzeugen.
In einer Webumgebung werden automatisch weitere PropertySources
für Daten aus der web.xml
registriert. Wahlweise kann auch eine JndiPropertySource
aktiviert und auch eigenePropertySources
implementiert und der Umgebung hinzugefügt werden.
Nutzung der Properties
Bisher haben wir einen Anwendungsfall für Properties gesehen, nämlich die Property ’spring.profiles.active‘, die von Spring genutzt wird, um die aktiven Umgebungsprofile zu setzen. In Listing 1 haben wir gesehen, wie man in einer Spring-Konfiguration mithilfe des Tags property-placeholder
Platzhalter mit Werten aus einer Properties-Datei ersetzen kann. Ab Spring 3.1 wird nun bei Verwendung des Tags der PropertySourcesPlaceholderConfigurer
registriert, der zunächst in den definierten Dateien sucht und danach die PropertySources
des Environments
durchgeht. Auch bei der Platzhalterersetzung im import-Tag werden nun alle PropertySources
durchsucht. Wir haben insgesamt also eine höhere Flexibilität, da die Werte aus beliebigen Quellen stammen können.
Fazit
Die Environment Abstraction fasst bereits bestehende und neue Funktionalitäten für das Umgebungsmanagement auf sinnvolle und erweiterbare Art und Weise zusammen. Die Bean Definition Profiles ermöglichen es, Beans bestimmten Profilen zuzuweisen. Nur wenn die Profile aktiviert werden, werden auch die Bean-Definitionen geladen und die Beans erstellt. Eine PropertySource
ist eine beliebige Quelle von Properties. Jedes Environment beinhaltet eine Hierarchie von PropertySources
, die frei konfigurierbar und erweiterbar ist, aber sehr sinnvolle Defaults enthält. Zusammenfassend ist festzustellen, dass dieses Feature eine bisher vorhandene Lücke in der Konfiguration von Spring-Anwendungen mit der für Spring typischen Offenheit für Erweiterungen schließt.
Weiter geht’s in Kürze mit Teil 2. Darin behandeln wir dann das Thema Cache Abstraction. Stay tuned!
Pascal Czollmann ist als Senior Software Engineer bei dem IT-Dienstleistungs- und Beratungsunternehmen adesso AG beschäftigt. Er arbeitet dort seit mehreren Jahren in unterschiedlichen Kundenprojekten im Java-Enterprise-Umfeld.
Hinterlasse einen Kommentar