CDI-Contexts and Depandency Injection (Java EE)

Willemers Informatik-Ecke

2017-05-18

Depency Injection

Diese Seite verwendet das Beispiel der Website https://dzone.com/articles/cdi-di-p1 .

Klassen verwenden Dienste anderer Klassen, deren Implementierung sie in der Regel nicht interessiert. Aus diesem Grund gibt es das Prinzip der "Programmierung gegen Schnittstellen statt Implementierungen". Kennzeichnend ist, dass beim Aufruf von new der LValue (also das Ergebnis der Zuweisung) als Typ des Interfaces angegeben wird.

// Es gilt: InternetTransport implements Transport
Transport meinTransport = new InternetTransport();
Durch den Befehl new wird meinTransport mit dem InternetTransport verbunden. Der Austausch von InternetTransport durch etwa LocalAreaTransport ist leicht möglich, indem der Klassenname nach dem new ersetzt wird. Allerdings gilt diese Flexibilität nur vor dem Übersetzen. Nach der Kompilierung ist diese Änderung nicht mehr möglich.

Will man eine flexible Kopplung, können folgende Wege beschritten werden:

  • Man könnte die Kopplung über den Klassennamen als Zeichenkette erreichen, so wie man es etwa bei den JDBC-Treibern tut. Das ist zwar flexibler, aber nicht typsicher. Es ist nicht einmal sicher, dass es überhaupt eine solche Klasse gibt.
  • Man kann eine Setter-Methode für meinTransport anlegen, die als Parameter Transport erwartet. So können zur Laufzeit beliebige Transport-Implementierungen zugewiesen werden. Eine Default-Vorgabe könnte per Konstruktor-Parameter erledigt werden.
Damit CDI funktioniert muss immer eine Datei beans.xml im META-INF existieren. Sie kann ggf sogar leer sein. Besser ist allerdings ein Rahmen wie dieser:
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">

</beans>

@Inject

Mit der Annotation @Inject wird der Ort der Injektion markiert. Das kann das Attribut sein, die Setter-Methode oder auch der Parameter des Konstruktors.

Ein Attribut wird auf die folgende Weise zur Injektion markiert.

@Inject Transport meinTransport;

Sie können auch die Setter-Methode mit @Inject markieren. Das hat den Vorteil, dass Sie sofort mit dem injizierten Wert arbeiten können.

Transport meinTransport;

@Inject
public void setMeinTransport(Transport transport) {
    meinTransport = transport;
}
Klassen, die injiziert werden sollen, müssen einen parameterlosen Konstruktor besitzen.

@Default

Eine Implementierung des Interfaces kann mit @Default versehen werden. Dann wird automatisch diese Klasse als Implementierung der mit @Inject vorgegebenen Stelle verwendet.

Prinzipiell kann @Default auch weggelassen werden, da dies die Vorgabe ist. Eine Klasse, die nicht Default sein soll, muss mit @Alternative markiert werden.

@Default
public class InternetTransport implements Transport {

Gibt es mehr als eine Default-Klasse, führt dies zu einem ExceptionInInitializerError. Alternativ mögliche Implementierungen von Transport müssen also mit @Alternative markiert werden.

@Alternative

Implementierungen des Interfaces, die nicht als Vorgabe verwendet werden sollen, müssen explizit als @Alternative markiert werden.
@Alternative
public class LocalAreaTransport implements Transport {

@Produces

Für die Erzeugung eines injizierten Objekts können Sie auch eine Factory verwenden. Dazu markieren Sie die Factory-Methode mit der Annotation @Produces.
@Produces
Transport createTransport() {

Prüfung bei Deploy

Hier wird der Typ geprüft, sobald die Klassen deployed werden, also auf dem Server eingerichtet. Die Kontrolle erfolgt also nicht erst zur Laufzeit, sondern bei der Einrichtung.
imoprt javax.inject.Inject;

public class Blub ... {
    @Inject
    private EineKlasse injizierteReferenz;


Homepage (C) Copyright 2017 Arnold Willemer