SeamFramework.orgCommunity Documentation
Web Beans riutilizza l'architettura base degli interceptor di EJB3.0, estendendo la funzionalità in due direzioni:
Qualsiasi Web Bean può avere interceptor, non solo i session bean.
Web Bean fornisce un più sofisticato approccio basato su annotazioni per associare interceptor ai Web Beans.
"La specifica Web Bean definisce due tipi di punti di intercettazione:
intercettazione del metodo di business, e
intercettazione della chiamata del ciclo di vita
Un interceptor di un metodo di business si applica alle invocazioni di metodi del Web Bean da parte di client del Web Bean:
public class TransactionInterceptor {
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
Un interceptor di chiamata del ciclo di vita si applica alle invocazioni delle chiamate del ciclo di vita da parte del container:
public class DependencyInjectionInterceptor {
@PostConstruct public void injectDependencies(InvocationContext ctx) { ... }
}
Una classe interceptor può intercettare entrambi le chiamate del ciclo di vita ed i metodi di business.
Si supponga di voler dichiarare transazionali alcuni Web Beans. La prima cosa necessaria è un'annotazione di interceptor binding per specificare esattamente quali Web Beans sono interessati:
@InterceptorBindingType
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {}
Ora è facilmente possibile specificare che ShoppingCart
è un oggetto transazionale:
@Transactional
public class ShoppingCart { ... }
O se si preferisce, si può specificare che solo un metodo sia transazionale:
public class ShoppingCart {
@Transactional public void checkout() { ... }
}
Bene, ma da qualche parte è necessario implementare l'interceptor che fornisce l'aspect di gestione della transazione. Occore quindi creare un interceptor EJB standard e annotarlo con @Interceptor
e @Transactional
."
@Transactional @Interceptor
public class TransactionInterceptor {
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
Tutti gli interceptor dei Web Beans sono Web Beans semplici e possono sfruttare la dependency injection e la gestione del ciclo di vita contestuale.
@ApplicationScoped @Transactional @Interceptor
public class TransactionInterceptor {
@Resource Transaction transaction;
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
Diverso interceptor possono usare lo stesso tipo di interceptor binding.
Infine occorre abilitare l'interceptor in web-beans.xml
.
<Interceptors>
<tx:TransactionInterceptor/>
</Interceptors
>
Ma perché viene usato ancora XML, quando Web Beans non dovrebbe utilizzarlo?
La dichiarazione XML risolve due problemi:
Ci consente di specificare un ordinamento totale per tutti gli interceptor del sistema, assicurando un comportamente deterministico, e
consente di abilitare o disabilitare le classi di interceptor a deployment time
Per esempio è possibile specificare che l'interceptor di sicurezza venga eseguito prima di TransactionInterceptor
."
<Interceptors>
<sx:SecurityInterceptor/>
<tx:TransactionInterceptor/>
</Interceptors
>
Oppure si può disattivarli entrambi dal proprio ambiente di test!
Si supponga di voler aggiungere qualche informazione extra all'annotazione @Transactional
:
@InterceptorBindingType
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {
boolean requiresNew() default false;
}
Web Beans utilizzerà il valore di requiresNew
per scegliere tra due diversi interceptor, TransactionInterceptor
e RequiresNewTransactionInterceptor
.
@Transactional(requiresNew=true) @Interceptor
public class RequiresNewTransactionInterceptor {
@AroundInvoke public Object manageTransaction(InvocationContext ctx) { ... }
}
Ora è possibile usare RequiresNewTransactionInterceptor
in questo modo:
@Transactional(requiresNew=true)
public class ShoppingCart { ... }
Ma cosa succede se si ha solo un interceptor e si vuole che il manager ignori il valore di requiresNew
quando si associa l'interceptor? Si può usare l'annotazione @NonBinding
:
@InterceptorBindingType
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Secure {
@NonBinding String[] rolesAllowed() default {};
}
Solitamente si usano combinazioni di tipi di interceptor binding per associare pià interceptor ad un Web Bean. Per esempio, la seguente dichiarazione verrebbe impiegata per associare TransactionInterceptor
e SecurityInterceptor
allo stesso Web Bean:
@Secure(rolesAllowed="admin") @Transactional
public class ShoppingCart { ... }
Comunque in casi molto complessi un interceptor da solo potrebbe specificare alcune combinazioni di tipi di interceptor binding:
@Transactional @Secure @Interceptor
public class TransactionalSecureInterceptor { ... }
Allora quest'interceptor potrebbe venire associato al metodo checkout()
usando una delle seguenti combinazioni:
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() { ... }
}
Una limitazione del supporto del linguaggio Java per le annotazioni è la mancanza di ereditarietà delle annotazioni. In verità le annotazioni dovrebbero avere il riutilizzo predefinito per consentire che questo avvenga:
public @interface Action extends Transactional, Secure { ... }
Fortunatamente Web Beans provvede a questa mancanza di Java. E' possibile annotare un tipo di interceptor binding con altri tipi di interceptor binding. Gli interceptor binding sono transitivi qualsiasi Web Bean con il primo interceptor binding eredita gli interceptor binding dichiarati come meta-annotazioni.
@Transactional @Secure
@InterceptorBindingType
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action { ... }
Ogni Web Bean annotato con @Action
verrà legato ad entrambi TransactionInterceptor
e SecurityInterceptor
. (E anche TransactionalSecureInterceptor
, se questo esiste.)
L'annotazione @Interceptors
definita dalla specifica EJB è supportata per entrambi i Web Bean semplici ed enterprise, per esempio:
@Interceptors({TransactionInterceptor.class, SecurityInterceptor.class})
public class ShoppingCart {
public void checkout() { ... }
}
Comunque, quest'approccio soffre dei seguenti difetti:
l'implementazione degli interceptor è codificata nel codice di business,
gli interceptor possono non essere facilmente disabilitati a deployment time, e
l'ordinamento degli interceptor è non-globale è determinata dall'ordine in cui gli interceptor sono elencati al livello di classe.
Quindi si raccomanda l'uso di interceptor binding di stile Web Beans.