SeamFramework.orgCommunity Documentation

Capítulo 9. Interceptadores

9.1. Bindings de interceptadores
9.2. Implementando interceptadores (interceptors)
9.3. Habiliatando interceptadores (interceptors)
9.4. Vinculando interceptadores com membros
9.5. Múltiplas anotações de vinculação de interceptadores
9.6. Herança de tipos vinculadores de interceptadores
9.7. Uso de @Interceptors

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:

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:

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:

Portanto, recomendamos o uso de vinculadores de interceptadores no estilo da CDI.