SeamFramework.orgCommunity Documentation

Kapitel 7. Interzeptoren

7.1. Interzeptor-Bindings
7.2. Implementierung von Interzeptoren
7.3. Interzeptoren aktivieren
7.4. Interzeptor-Bindings mit Mitgliedern
7.5. Multiple Interzeptor bindende Annotationen
7.6. Vererbung von Interzeptor-Binding-Typen
7.7. Verwendung von @Interceptors

Web Beans verwenden die grundlegende Interzeptor-Architektur von EJB 3.0, wobei die Funktionalität in zwei Richtungen erweitert wird:

Die EJB-Spezifikation definiert zwei Arten von Abfangpunkten (sog. "Interception Points"):

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:

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:

Daher empfehlen wir die Verwendung von Interzeptor-Bindings im Web Beans Stil.