Schlagwort-Archive: JBoss Weld

Seperation von Anforderungen – DI mit JBoss Weld

Was Dependency Injection ist soll in diesem Blog nicht erläutert werden. Dazu sei z.B. auf den sehr guten Artikel von Nicholas Lesiecki auf IBM developerworks verwiesen. Ebenso werden hier nicht alle Möglichkeiten (wie z.B. Konstruktor-, Methoden- und Fieldinjektion) beschrieben. Hier geht es um die Lösung der Anforderungen aus dem einleitenden Blog mit Weld von JBoss. Weld ist dabei die Referenzimplementierung des JSR-299.

Der Use Case ist weiterhin die im ersten Artikel beschriebene Anforderung: Ein Kunde möchte die eindeutige Id um 4 Zeichen gekürzt haben, damit ein nachfolgender Prozess der NOMINT-Nachricht eigene Zeichen hinzufügen kann.
Zunächst definieren wir ein einfaches Interface IdGenerator.

public interface IdGenerator {

  String createUniqueId(Date date);

}

Nun wird je eine Implementierung für das Standardverhalten und für die vom Kunden gewünschte Umsetzung implementiert. Beide implementieren dazu das zuvor definierte Interface. Zunächst die Standardimplementierung.

/**
 * @author 7driods (FA)<br>
 *         Default implementation for Id generation.
 */
@Default
public class DefaultIdGenerator implements IdGenerator {

  public String createUniqueId(Date date) {
    SimpleDateFormat sdfUnique = new SimpleDateFormat("yyyyMMddHHmmssSSS");
    return sdfUnique.format(date).substring(3);
  }
}

Mit der Annotation @Default kann die Standardimplementierung markiert werden. Das ist jedoch nicht zwingend notwendig. Damit Dependency Injection mit Weld funktioniert, muss im Classpath im Verzeichnis META-INF die Datei beans.xml existieren. Sie hat für die Verwendung der Standardimplementierung das folgende Aussehen:

<?xml version="1.0" encoding="UTF-8"?>
<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>

Die Implementierung für den Kunden hat den gleichen Aufbau, mit der vom Kunden gewünschten Anpassung.

/**
 * @author 7droids.de (FA) <br>
 *         Custom implementation for Id generation.
 */
@Alternative
public class CustomIdGenerator extends DefaultIdGenerator {

  @Override
  public String createUniqueId(Date date) {
    String id = super.createUniqueId(date);
    return id.substring(2, id.length() - 2);
  }
}

Die Annotation @Alternative markiert hier eine alternative Implementierung die im Standard deaktiviert/nicht berücksichtigt wird. Soll diese vom Framework verwendet werden, muss sie explizit in der beans.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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">
  <alternatives>
    <class>de.sevendroids.java.cdi.weld.CustomIdGenerator</class>
  </alternatives>
</beans>

Nun muss nur noch die Klasse NomintFilter angepasst werden. Dazu wird eine neue Instanzvariable eingeführt, die mit der Annotation @Inject als durch Weld zu instanziierende Variable markiert wird (Zeile //1). Die Erzeugung der Id wird dann durch die Implementierung des Interfaces übernommen (Zeile //2).

public class NomintFilter {

  @Inject
  private IdGenerator generator; // 1
  …
  String uniqueId = generator.createUniqueId(now); // 2
  …
}

Wenn ein DI-Framework verwendet wird, kann die Instanziierung einer Klasse mit Dependencies nicht mehr über die „new“ Funktion erfolgen. Stattdessen wird die Instanziierung dem Framework überlassen. Für die Klasse NomintFilter sieht der Aufruf dann wie folgt aus:

NomintFilter filter = new Weld().initialize().instance()
    .select(NomintFilter.class).get();

Die Initialisierung von Weld sollte dabei sinnvollerweise nur einmal je JVM erfolgen.

Wie kann nun mit DI unter Verwendung von Weld zwischen der Standard- und der Kundenimplementierung gewechselt werden. In meinem Fall werden das Standardmodul und das Kundenmodul in einem eigenen Jar ausgeliefert. In jedem der Jars gibt es die Datei beans.xml in den oben angegebenen Ausprägungen. Über den Classpath wird dafür gesorgt, dass zunächst das Kundenmodul und damit die beans.xml des Kundenmoduls zuerst gelesen wird. Dadurch ist für den Kunden seine Implementierung aktiv. Andere Kunden, die die Standardimplementierung bekommen sollen, verwenden die beans.xml aus dem Standardmodul.

Nicht verschwiegen werden sollte der Aufwand für die Umstellung der Instanziierung der NomintFilter-Klasse über den oben beschriebenen Weg. Diese Umstellung sollte mit Hilfe der IDE erfolgen.

Die Klassen inklusive Unittests sind als Maven-Projekt im Git-Repository unter git://github.com/7droids/ProductBreakPoint.git verfügbar.

Im nächsten Artikel soll die Anforderung mit dem Dependency Injection Framework Guice von Google umgesetzt werden.