tech-trends

CDI DeltaSpike Partial-Bean

7 Min // 30-10-2018

DeltaSpike ist eine CDI Extension Bibliothek, die ihre Anfänge im Jahre 2011 hat und sich bis heute zu einem fixen Bestandteil einer CDI Anwendung entwickelt hat.

Was bringen mir Partial-Beans?

Das DeltaSpike Modul Partial-Bean ist dazu gedacht, um manuelle Implementierungen durch generische Händler abzulösen, um Boilerplate Code zu vermeiden. Mit dem Partial-Bean Modul können Schnittstellen und abstrakte Klassen über einen generischen Handler (java.lang.reflect.InvocationHandler) behandelt werden, wobei die Schnittstellen und abstrakten Klassen Metadaten bereitstellen, die vom generischen Handler verwendet werden. Voraussetzung ist, dass alle Methoden auf dieselbe Art und Weise behandelt werden können.

Was bringen mir Partial-Beans?

DeltaSpike Partial-Bean Modul stellt die Annotation @PartialBeanBinding zur Verfügung, die auf einer eigenen implementierten Annotation angeben wird, die ein Partial-Bean markiert, wobei @PartialBeanBinding für das Deltaspike Partial-Bean Modul als Marker Annotation dient. Die eigens implementierte Annotation wird auf einer Schnittstelle oder einer abstrakten Klasse, sowie dem generischen Handler angegeben, wobei die CDI Extension des Partial-Bean Moduls diese Schnittstellen, abstrakten Klassen, sowie den generischen Handler über CDI verfügbar macht. Das Partial-Bean kann dabei auch CDI Scopes definieren, die von der CDI Extension übernommen werden.

Das erstellte Partial-Bean ist im CDI Container verfügbar und kann in CDI Beans injiziert werden. Sollte das Partial-Bean auch mit @Named annotiert sein, so kann es auch z.B. in JSF Seiten über Java EL angesprochen werden.

//

ExceptionBuilder als Partial-Beans?

In jeder Anwendung können Fehler auftreten, die über Exceptions nach außen hin propagiert werden, wobei eine Exception Instanz erst erstellt und dann mit einer aussagekräftigen Nachricht versehen werden muss. Das Erstellen und Definieren von Ausnahmen wird überall in einer Anwendung vorzufinden sein und wird meistens, bis auf unterschiedliche Daten, sich immer wiederholden. Daher ist diese Art von Code ein gutes Beispiel für Boilerplate Code. Nicht nur, dass immer das gleiche implementiert wird, ist es auch schwer, so ein Code konsistent zu halten, und Änderungen werden schwer umzusetzen sein, ohne dass die gesamte Codebasis durchsucht werden muss.

In diesem Beispiel wollen wir eine Schnittstelle definieren, die über Annotationen Metadaten bereitstellt, die einen Fehler so weit wie möglich statisch definieren. Die Entwickler können in dieser Schnittstelle an einer Stelle alle möglichen Fehler definieren. Bevor wir diese Schnittstelle implementieren können, müssen wir die von Partial-Bean Modul benötigten Implementierungen bereitstellen, welche die Schnittstelle als Partial-Bean markieren und dessen Funktionalität bereitstellen.

Partial-Bean Annotation

Zuerst benötigen wir eine Annotation, welche die Schnittstelle als Partial-Bean markieren, wobei wir diese Annotation auch dazu verwenden, um auf der Schnittstelle Metadaten bereitzustellen.

@Target ({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@PartialBeanBinding
public @interface ErrorBuilderApi {
String module() default "";
String errorPrefix() default "ERROR";
}

 

Die Annotation @PartialBeanBinding wird in unserer Annotation angegeben, wobei alle Schnittstellen und abstrakten Klassen, die mit unserer Annotation markiert wurden, als Partial-Beans behandelt werden. Die Annotation @Inherited ist notwendig, da dieses Bean hinter einem Proxy steht, wobei der Datentyp des Proxies eine Ableitung des proxied Datentyps ist. Ohne die Annotation @Inherited wäre die Annotation nicht über den Proxy Datentyp erreichbar, sondern nur direkt über die Schnittstelle. Außerdem wurden die beiden Attribute module und errorPrefix definiert, welche in der Fehlernachricht verwendet werden. Dadurch, dass diese Daten innerhalb eines Partial-Bean statisch sind, können wir sie als Attribute der Annotation definieren.

Metadaten für die Methoden

Wir benötigen auch Metadaten für die Methoden, die einen speziellen Fehler beschreiben, die wir auch über eine eigene Annotation angeben.

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Error {

    String code();
    String message();
    Class<? extends Throwable> exceptionClass() 
                               default 
RuntimeException.class;
}

Die Attribute dieser Annotation beschreiben die statischen Aspekte des Fehlers, wie:

  • den Fehlercode (code),
  • die Fehlernachricht (message),
  • und der Datentyp der Exception.

Generischer Handler für das Partial-Bean

Partial-Beans benötigen einen generischen Handler, der die Methodenaufrufe proxied und die gemeinsame Logik implementiert. Der generische Handler implementiert die Schnittstelle java.lang.reflect.InvocationHandler,wobei diese Klasse ein vollwertiges CDI Bean ist, welches andere Beans injizieren kann, sowie auch Interceptoren und Decoratoren unterstützt.

@ApplicationScoped
@ErrorBuilderApi
public class ErrorBuilderInvocationHandler implements InvocationHandler {

@Override
public Object invoke(Object proxy,
Method method,
Object[] args) throws Throwable {
// Handler logic, to create ErrorBuilder instance
}
}

Die Beispielanwendung

Auf Github ist das beschriebene Beispiel unter https://github.com/cchet/deltaspike-sample-partial-bean verfügbar und kann wie folgt verwendet werden.

  • git clone https://github.com/cchet/deltaspike-sample-partial-bean.git
  • mvn clean install

Das Beispiel implementiert Tests, die das Partial-Bean mit DeltaSpike Test-Container-Control Modul sowie mit DeltaSpike Container-Control Modul testen, die in einem anderen Artikel beschrieben werden.

// Autor

Thomas Herzog, BSc.

Boyang Xia, BSc.