SeamFramework.orgCommunity Documentation
A funcionalidade interceptador é definida na especificação Java Interceptors. CDI melhora esta funcionalidade com uma abordagem mais sofisticada, semântica e baseada em anotações para associar interceptores aos beans.
A especificação Interceptors define dois tipos de pontos de interceptação:
interceptação de métodos de negócios, e
interceptadores de chamadas de ciclo de vida
Adicionalmente, a especificação EJB define a interceptação do estouro de tempo.
Um interceptador de método de negócio se aplica a invocações de métodos do bean por clientes do bean:
public class TransactionInterceptor {
@AroundInvoke
public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}
Um interceptador de chamadas de ciclo de vida se aplica a invocações após algum evento do ciclo de vida do bean pelo contêiner:
public class DependencyInjectionInterceptor {
@PostConstruct
public void injectDependencies(InvocationContext ctx) { ... }
}
Uma classe de interceptador pode interceptar tanto o ciclo de vida quanto métodos de negócio.
Um interceptador de estouro de tempo se aplica a invocações de métodos EJB limitadores de tempo pelo contêiner:
public class TimeoutInterceptor {
@AroundTimeout
public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}
Suponha que queremos declarar que algum de nossos beans são transacionais. A primeira coisa de que precisamos é um tipo vinculador de interceptador para especificar exatamente em quais beans estamos interessados:
@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {}
Agora podemos especificar facilmente que nosso ShoppingCart
é um objeto transacional:
@Transactional
public class ShoppingCart { ... }
Ou, se preferirmos, podemos especificar que apenas um método é transacional:
public class ShoppingCart {
@Transactional public void checkout() { ... }
}
Isto é ótimo, mas em algum momento teremos que implementar o interceptador que fornece este aspecto de gerenciamento transacional. Tudo que precisamos fazer é criar um interceptador normal, e anotá-lo com @Interceptor
e @Transactional
.
@Transactional @Interceptor
public class TransactionInterceptor {
@AroundInvoke
public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}
Interceptadores podem tirar vantagem da injeção de dependência:
@Transactional @Interceptor
public class TransactionInterceptor {
@Resource UserTransaction transaction;
@AroundInvoke
public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}
Vários interceptadores podem usar o mesmo tipo vinculador.
Por padrão, todos os interceptadores estão desabilitados. Nós precisamos habilitar nosso interceptador no descritor beans.xml
de um arquivo de beans. Esta ativação somente se aplica aos beans neste arquivo.
<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">
<interceptors>
<class
>org.mycompany.myapp.TransactionInterceptor</class>
</interceptors>
</beans
>
Espere! Porque esse monte de elementos?
Bem, a declaração XML é atualmente uma boa coisa. E resolve dois problemas:
o que nos possibilita especificar totalmente a ordem para todos os interceptores em nosso sistema, garantindo um comportamento determinístico, e
o que nos permite habilitar ou desabilitar classes de interceptador em tempo de implantação.
Por exemplo, poderíamos especificar que nosso interceptador de segurança rode antes de nosso interceptador transacional.
<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">
<interceptors>
<class
>org.mycompany.myapp.SecurityInterceptor</class>
<class
>org.mycompany.myapp.TransactionInterceptor</class>
</interceptors>
</beans
>
Ou poderíamos desabilitá-los em nosso ambiente de teste apenas não os mencionando em beans.xml
! Ah, tão simples.
Suponhamos que queremos acrescentar algumas informações adicionais para o nossa anotação @Transactional
:
@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Transactional {
boolean requiresNew() default false;
}
CDI utilizará o valor de requiresNew
para escolher entre fois diferentes interceptadores, TransactionInterceptor
e RequiresNewTransactionInterceptor
.
@Transactional(requiresNew = true) @Interceptor
public class RequiresNewTransactionInterceptor {
@AroundInvoke
public Object manageTransaction(InvocationContext ctx) throws Exception { ... }
}
Agora podemos usar RequiresNewTransactionInterceptor
assim:
@Transactional(requiresNew = true)
public class ShoppingCart { ... }
Mas e se temos somente um interceptador e queremos que o contêiner ignore o valor de requiresNew
ao vincular interceptadores? Talvez esta informação seja útil somente para a implementação do interceptador. Nós podemos usar a anotação @Nonbinding
:
@InterceptorBinding
@Target({METHOD, TYPE})
@Retention(RUNTIME)
public @interface Secure {
@Nonbinding String[] rolesAllowed() default {};
}
Usualmente usamos combinações de tipos vinculadores de interceptadores para ligar múltiplos interceptadores a um bean. Por exemplo, a seguinte declaração poderia seria usada para vincular TransactionInterceptor
e SecurityInterceptor
ao mesmo bean:
@Secure(rolesAllowed="admin") @Transactional
public class ShoppingCart { ... }
No entanto, em casos muito complexos, um interceptador pode em si mesmo especificar algumas combinações de tipos vinculadores de interceptadores:
@Transactional @Secure @Interceptor
public class TransactionalSecureInterceptor { ... }
Então este interceptador poderia ser amarrado ao método checkout()
usando qualquer uma das seguintes combinações:
public class ShoppingCart {
@Transactional @Secure public void checkout() { ... }
}
@Secure
public class ShoppingCart {
@Transactional public void checkout() { ... }
}
@Transactional
public class ShoppingCart {
@Secure public void checkout() { ... }
}
@Transactional @Secure
public class ShoppingCart {
public void checkout() { ... }
}
Uma limitação do suporte a anotações na linguagem Java é a falta de herança em anotações. Realmente, anotações poderiam ter esse reuso embutido, permitindo este tipo de coisa funcionasse:
public @interface Action extends Transactional, Secure { ... }
Bem, felizmente, CDI contorna a falta desta funcionalidade em Java. Podemos anotar um tipo vinculador com outros tipos vinculadores de interceptadores (denominado como uma meta-anotação). As vinculações de interceptadores são transitivos — qualquer bean com o primeiro vínculo a um interceptador herda os vínculos a interceptadores declarados como meta-anotações.
@Transactional @Secure
@InterceptorBinding
@Target(TYPE)
@Retention(RUNTIME)
public @interface Action { ... }
Agora, qualquer bean anotado com @Action
será amarrado tanto a TransactionInterceptor
quanto a SecurityInterceptor
. (E mesmo a TransactionalSecureInterceptor
, se estiver presente.)
A anotação @Interceptors
definida pela especificação de interceptadores (e utilizada pelas especificações de managed bean e EJB) é ainda suportada em CDI.
@Interceptors({TransactionInterceptor.class, SecurityInterceptor.class})
public class ShoppingCart {
public void checkout() { ... }
}
No entanto, esta abordagem sofre os seguintes inconvenientes:
a implementação do interceptador é duramente codificado no código de negócio,
os interceptadores podem não ser facilmente disabilitados em tempo de implantação, e
a ordem dos interceptadores não é global—ela é determinada pela ordem em que os interceptadores são listados a nível de classe.
Portanto, recomendamos o uso de vinculadores de interceptadores no estilo da CDI.