SeamFramework.orgCommunity Documentation
Web Beans verwenden die grundlegende Interzeptor-Architektur von EJB 3.0, wobei die Funktionalität in zwei Richtungen erweitert wird:
Jedes Web Bean kann Interzeptoren besitzen, nicht nur Session Beans.
Web Beans bieten eine fortgeschrittenere auf Annotationen basierende Vorgehensweise bei der Bindung von Interzeptoren an Web Beans.
Die EJB-Spezifikation definiert zwei Arten von Abfangpunkten (sog. "Interception Points"):
Business Methoden Interception und
Lebenszyklus-Callback Interception.
Ein Business Methoden Interzeptor gilt für Aufrufe von Methoden des Web Beans durch Clients des Web Beans:
public class TransactionInterceptor {
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
Ein Lebenszyklus Callback-Interzeptor gilt für Aufrufe von Lebenszyklus Callbacks durch den Container:
public class DependencyInjectionInterceptor {
@PostConstruct public void injectDependencies(InvocationContext ctx) { ... }
}
Eine Interzeptorklasse kann sowohl Lebenszyklus-Callbacks als auch Business-Methoden abfangen.
Nehmen wir an, wir wollten deklarieren, dass einige unserer Web Beans transaktional sind. Das erste, was wir benötigen ist eine Interzeptor bindende Annotation, um festzulegen, für welches Web Bean wir uns interessieren:
@InterceptorBindingType
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {}
Jetzt können wir ganz leicht unser ShoppingCart
als ein transaktionales Objekt festlegen:
@Transactional
public class ShoppingCart { ... }
Oder, falls uns das lieber ist, können wir festlegen, dass nur eine Methode transaktional ist:
public class ShoppingCart {
@Transactional public void checkout() { ... }
}
Das ist toll, aber irgendwann müssen wir den den Managementaspekt dieser Transaktion liefert, implementieren. Wir müssen nur einen standardmäßigen EJB-Interzeptor erstellen und ihn mit @Interceptor
und @Transactional
annotieren.
@Transactional @Interceptor
public class TransactionInterceptor {
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
Bei allen Web Beans Interzeptoren handelt es sich um einfache Web Beans und sie können "Dependency"-Einspeisung und kontextuelles Lebenszyklus-Management nutzen.
@ApplicationScoped @Transactional @Interceptor
public class TransactionInterceptor {
@Resource Transaction transaction;
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
Mehrere Interzeptoren können denselben Interzeptor Binding-Typ verwenden.
Schließlich müssen wir unseren Interzeptor in web-beans.xml
aktivieren.
<Interceptors>
<tx:TransactionInterceptor/>
</Interceptors
>
Puh! Warum diese Suppe an Klammern?
Nun, die XML-Deklaration löst zwei Probleme:
Sie ermöglicht es uns, eine totale Reihenfolge für alle Interzeptoren in unserem System festzulegen, wodurch deterministisches Verhalten festgelegt wird und
Interzeptor-Klassen zum Zeitpunkt des Deployments aktiviert oder deaktiviert werden können.
Zum Beispiel könnten wir festlegen, dass unser Sicherheitsinterzeptor vor unserem TransactionInterceptor
ausgeführt wird.
<Interceptors>
<sx:SecurityInterceptor/>
<tx:TransactionInterceptor/>
</Interceptors
>
Oder wir könnten sie beide in unserer Testumgebung abschalten!
Nehmen wir an, wir wollten unserer @Transactional
-Annotation weitere Informationen hinzufügen:
@InterceptorBindingType
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {
boolean requiresNew() default false;
}
Web Beans verwendet den Wert von requiresNew
zur Auswahl zwischen zwei verschiedenen Interzeptoren TransactionInterceptor
und RequiresNewTransactionInterceptor
auszuwählen.
@Transactional(requiresNew=true) @Interceptor
public class RequiresNewTransactionInterceptor {
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
Jetzt können wir RequiresNewTransactionInterceptor
wie folgt verwenden:
@Transactional(requiresNew=true)
public class ShoppingCart { ... }
Was aber, wenn wir über nur einen Interzeptor verfügen und wir wollen, dass der Manager bei der Bindung der Interzeptoren den Wert von requiresNew
ignoriert? Wir können die @NonBinding
-Annotation verwenden:
@InterceptorBindingType
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Secure {
@NonBinding String[] rolesAllowed() default {};
}
In der Regel verwenden wir Kombinationen von Interzeptor-Binding-Typen, um mehrere Interzeptoren an ein Web Bean zu binden. Folgende Deklaration etwa würde verwendet, um TransactionInterceptor
und SecurityInterceptor
an dasselbe Web Bean zu binden:
@Secure(rolesAllowed="admin") @Transactional
public class ShoppingCart { ... }
In sehr komplexen Fällen aber kann ein Interzeptor selbst eine Kombination von Interzeptor-Binding-Typen festlegen:
@Transactional @Secure @Interceptor
public class TransactionalSecureInterceptor { ... }
Dann könnte dieser Interzeptor an die checkout()
-Methode gebunden werden, indem eine der folgenden Kombinationen verwendet wird:
public class ShoppingCart {
@Transactional @Secure public void checkout() { ... }
}
@Secure
public class ShoppingCart {
@Transactional public void checkout() { ... }
}
@Transactionl
public class ShoppingCart {
@Secure public void checkout() { ... }
}
@Transactional @Secure
public class ShoppingCart {
public void checkout() { ... }
}
Eine Einschränkung des Java Sprach-Supports für Annotationen ist das Fehlen von Annotationsvererbung. Eigentlich sollten Annotationen eine eingebaute Wiederverwendung besitzen, damit diese Art von Sache funktioniert:
public @interface Action extends Transactional, Secure { ... }
Nun, zum Glück umgeht Web Beans dieses fehlende Feature von Java. Wir können einen Interzeptor Binding-Typ mit anderen Interzeptor Binding-Typen annotieren. Die Interzeptor-Bindings sind transitive jedes Web Bean mit demselben Interzeptor-Binding erbt die als Meta-Annotationen deklarierten Interzeptor-Bindings.
@Transactional @Secure
@InterceptorBindingType
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action { ... }
Jedes mit @Action
annotierte Web Bean wird sowohl an TransactionInterceptor
als auch SecurityInterceptor
gebunden. (Und sogar an TransactionalSecureInterceptor
, falls es existiert).
Die durch die EJB-Spezifikation definierte @Interceptors
-Annotation wird sowohl für Enterprise als auch einfache Web Beans unterstützt, zum Beispiel:
@Interceptors({TransactionInterceptor.class, SecurityInterceptor.class})
public class ShoppingCart {
public void checkout() { ... }
}
Allerdings besitzt diese Vorgehensweise folgende Nachteile:
Die Interzeptorimplementierung ist im Business Code hardkodiert,
Interzeptoren können zum Deployment-Zeitpunkt nicht einfach deaktiviert werden und
Die Interzeptorreihenfolge ist nicht allgemeingültig sie wird durch die Reihenfolge, in der Interzeptoren auf Klassenebene aufgeführt sind, festgelegt.
Daher empfehlen wir die Verwendung von Interzeptor-Bindings im Web Beans Stil.