CDI: Leichtgewichtige Injection in Java EE 6

Bei der Umsetzung von Java Enterprise Projekten steht man (besser) früher oder (schlechter) später vor der Wahl eines geeigneten Komponenten-Frameworks. In der Vergangenheit fiel die Wahl dabei häufig auf Spring, da es gegenüber EJB, dem Java Standard, lange leichtgewichtiger und moderner daherkam. Mit EJB3 hatte sich dies im Java EE 5 bereits ein wenig geändert, da nun eine Konfiguration über Annotations ermöglicht und die Anzahl der notwendigen Interfaces reduziert wurde. Trotzdem blieb EJB ein Schwergewicht, weil es nur auf den grossen, umfangreichen und komplexen Java EE Applikationsservern eingesetzt werden konnte. Dies war für viele Projekte ein schlagender Grund um auf EJB zu verzichten.

Für jeden das passende Profil

Mit Java EE 6, das seit Ende 2009 verfügbar ist, hat Sun bzw. inzwischen Oracle mächtig in Sachen Leichtigkeit aufgeholt. Die Applikationsserver müssen nun nicht mehr den kompletten Satz an Features des Standards unterstützen. Stattdessen wurden „Profiles” eingeführt, spannend ist im Umfeld der Web Applications natürlich das „Web Profile”. Die Tabelle bei ehemals Sun gibt einen guten Überblick, welche Features dort unterstützt werden und welche entfallen. Klar, die Reduktion kostet Opfer: Gerade Integrations-Technologien wie JMS, JCA, JAX-WS oder JAX-RS fallen weg.
Dafür wird aber der gängige Stack für Web Applications vollumfänglich unterstützt, sowohl in View (Servlet, JSF2), Komponenten (EJB, CDI) als auch Persistenz (JPA, Bean Validation). Eine solche Web Application kann auch, und das ist neu in Java EE 6, als WAR Datei deployed werden. Vorbei die Zeiten der XML-überfluteten EARs. EJB3.1 ist als Lite Ausprägung im Web Profile enthalten. Lite bedeutet u.a. den Verzicht auf MessageDriven Beans und die neue asynchrone Ausführung von Methoden. Die Application Server können sich mit dem neuen Profil deutlich schlanker formen und reduzieren damit sowohl Administrationsaufwand als auch Betriebskosten.

Diese neu gewonnene Attraktivität von Java EE ist spürbar: Auf Konferenzen wie der W-JAX 2010 war das Interesse an den Standard Frameworks sehr gross. In Workshops wie dem Java EE 6 Roundtrip von Adam Bien konnte man froh sein, einen Sitzplatz zu ergattern.

Ein neues Komponentenmodell

Ein wichtiger Bestandteil von Java EE 6 Web Profile ist die „Contexts and Dependency Injection”, kurz CDI. Hierbei handelt es sich um ein weiteres Komponentenmodell, also ähnlich wie EJB. CDI verfolgt aber andere Ziele: Es enthält kein automatisches Transaktionshandling, hat keine Remote und Local Interfaces und hält hinsichtlich der mitgelieferten Features sicherlich keinen Vergleich zu EJB stand.

Dafür besitzt es aber eine nochmals erhöhte Flexibilität, die gerade bei Web Applications in vielen Schichten sinnvoll ist. Denn, soviel sei vorweg genommen, es ist durchaus ein gangbarer und oft sinnvoller Weg, CDI mit EJB zu kombinieren. Beispielsweise könnte eine JSF2 View an einen CDI View Layer gekoppelt sein. Die Business Logik im nachgelagerten Layer kann dann transaktionssicher per EJB gekapselt sein. Die danach folgende Persistenz kann wieder per CDI flexibel gehandhabt werden. Dies ist, wie gesagt, nur ein Beispiel; trotzdem zeigt es bereits einerseits wie flexibel auf die Anforderungen des Kundensystems eingegangen werden kann, andererseits aber auch dass bei der Architektur des Systems Beratung zwingend notwendig ist.

Ganz kurzer Ausritt in die Theorie: CDI wird durch den JSR-299 spezifiziert. Dieser Standard bildet einen Layer auf dem JSR-330 „Dependency Injection”. Man benutzt also eigentlich beide Standards.

Einbindung von CDI in die Web Application

Zurück zur Praxis. Um eine Library vom Container bzw. dessen CDI-Implementierung (Referenz ist JBoss Weld) für CDI zu scannen, muss eine beans.xml Datei angelegt werden. Keine Angst, die „Hell of XML” bricht nicht aus, denn die Datei bleibt in der Regel bis auf das Root-Element leer. Sie dient nur als Notifizierung dass die Library (kann auch ein JAR sein) gescannt werden soll.

CDI besteht wie üblich aus Scopes und Beans. Die Steuerung erfolgt über Annotations. Eine Bean kann per @Named zum CDI Kontext hinzugefügt werden. Sie ist dann per Standard im Request Scope. Über gezieltes Scoping wie @SessionScoped kann eine Bean auch mehrere Requests überleben. Interessant ist dabei, dass im Gegensatz zu anderen Komponentenmodellen, z.B. JSF2 @ManagedBean, erzwungen wird dass eine passivierbare Bean, also zum Beispiel @SessionScoped, java.io.Serializable implementiert. Ist dies nicht der Fall, versagt CDI / Weld den Start. Dies beugt vielen unangenehmen Überraschungen vor, falls später einmal Sessions passiviert und wiederhergestellt werden.

@Named
public class Addition {
    // ...
}

Wenn hier kein Parameter an die Annotation hinzugefügt wird, wird sie automatisch mit dem Klassennamen benannt, hier also “addition”. Man kann aber via Parameter den Namen auch selbst festlegen.

Der umgekehrte Weg, also die Nutzung einer im CDI Kontext registrierten Bean, kann auf mehrere Wege geschehen. Innerhalb einer anderen CDI Bean kann per @Inject Annotation darauf zugegriffen werden. Die Deklaration erfolgt typsicher. Der Container wird bei Initialisierung dieser Bean entweder eine im Scope vorhandene Bean injecten oder eine neue Bean erzeugen und an diese Stelle referenzieren.

    @Inject
    private Addition addition;

Wie man sieht wurde kein textueller Name bei der Annotation genutzt. Es reicht die Typangabe, um die Bean zu lokalisieren. Dabei kann es durchaus Fälle geben, in denen mehrere Beans vom gleichen Typ im Kontext vorhanden sind. Es gibt jedoch auch hier Mittel, typsicher die richtige Variante herauszufinden. Dazu später mehr.

Die Injection funktioniert auch innerhalb von Servlets, hier ein Beispiel in Servlet API 3.0:

@WebServlet(name="CDIServlet", urlPatterns={"/cdi"})
public class CDIServlet extends HttpServlet {
    
    @Inject
    private Addition addition;
    // ...
}

Da verwundert es nicht, dass auch ein Zugriff aus der Expression Language in JSF2 möglich ist:

    <h:outputText value="#{addition.text}" />

Hier muss nun erstmals der mit @Named implizit eingeführte Name der Bean genutzt werden. EL ist leider nicht typsicher.

Und so geht es weiter

Mit den nun vorgestellten Mitteln können Beans in CDI registriert sowie wieder referenziert werden. Dies ist jedoch nur die Grundlage für erweiterte Features, welche CDI dann richtig interessant machen. Im in Kürze folgenden Teil geht es daher tiefer in den Code: Wie man Beans ohne @Named und aus Methoden heraus in CDI erstellt, wie man Mengen von Beans injected und damit einen echten Plugin Mechanismus baut, sowie wie man mehrere Beans der gleichen Klasse im Kontext trotzdem typsicher unterscheiden kann.

Bis dahin beantworte ich gerne Rückfragen rund um CDI und/oder Java EE 6.

2 Gedanken zu “CDI: Leichtgewichtige Injection in Java EE 6

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>