O Contexto e a Injeção de Dependência ou Contexts and Dependency Injection (CDI) está definido na especificação JSR 346 que pode ser baixada no site da Oracle.

A especificação CDI basicamente define um mecanismo seguro de injeção de dependência na plataforma Java EE. O CDI já está disponível nos servidores Java EE 6 e posteriores. Se estivermos utilizando Tomcat, Jetty ou outro servidor antes do Java EE 6, ainda é possível habilitar o CDI copiando e configurando o JAR de alguma de suas implementações. A implementação de referência é a Weld disponível em http://seamframework.org/Weld sendo a mais usada e já embutida no JBoss e no Glassfish.

Quando habilitamos o CDI, praticamente todas as classes do projeto são consideradas Managed Beans e, portanto, passíveis de injeção e de serem injetadas. Podemos usar com CDI toda classe pública com um construtor público padrão ou algum que receba parâmetros e esteja anotado com @Inject.

A anotação @Inject é considerada a base de todo o CDI, pois ela permite a injeção de dependência e deve ser usada nos pontos em que desejamos a injeção automática, seja construtores, setters ou atributos privados.

Uma solicitação para uma injeção não necessita saber sobre o ciclo de vida real, implementação concreta, modelos de threads, ou outros de clientes do bean. Essa combinação de tipagem forte e baixo acoplamento nos remete a um código mais fácil de manter. O bean injetado tem um ciclo de vida bem definido. O bean injetado também é chamado de uma instância contextual, pois é sempre injetado num contexto. Praticamente qualquer POJO pode ser injetado como um bean CDI. Isto inclui EJBs, recursos JNDI, classes de entidade, unidades de persistência e contextos. Mesmo os objetos que foram criados anteriormente por um factory method (método de fábrica) podem agora ser facilmente injetados. O CDI também permite que componentes EJB possam ser usados, como JSF Managed Beans, fazendo uma ponte entre a camada transacional e a camada web.

Descoberta de Beans

As classes bean são implantadas em arquivos bean. Um arquivo bean tem os modos de descobertas conforme abaixo:

  • all: Todos os tipos no arquivo são considerados para injeção.
  • annotated: Apenas tipos com anotações definidas no bean serão considerados para injeção.
  • none: CDI está desabilitado.

Um arquivo de bean que não contém o arquivo beans.xml mas contém uma ou mais classes de bean com uma anotação de definição de bean, ou então um ou mais beans de sessão, é considerado um arquivo de bean implícito. Um bean com um tipo de escopo declarado é dito como tendo uma anotação de definição do bean.

Um arquivo de bean explícito é aquele que contém um arquivo beans.xml com ou sem número de versão, como exemplificado na Listagem 1.

Listagem 1. Definindo um arquivo de bean explicitamente.

  <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee">
  </beans>

Um arquivo de bean explícito também pode ter um arquivo beans.xml vazio, ou ainda com um número de versão 1.1 ou menor, com o modo de descoberta do bean do tipo all conforme mostra o código da Listagem 2.

Listagem 2. Definindo um arquivo de bean explicitamente com número de versão e modo de descoberta.

  <beans
           xmlns="http://xmlns.jcp.org/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
           bean-discovery-mode="all">
  </beans>

Podemos verificar acima um exemplo de um arquivo beans.xml contendo tags com abertura e fechamento, além de namespaces e atributos. Um arquivo de bean explícito tem o modo de descoberta (bean-discovery-mode) setado como all.

Um arquivo bean que contém um arquivo beans.xml com a versão 1.1 ou menor deve especificar o atributo bean-discovey-mode. O valor default para o atributo é annotated. Se este arquivo existe, então deve estar presente nas seguintes localizações:

  • No diretório "WEB-INF" ou "WEB-INF/classes/META-INF" para aplicações web;
  • No diretório “META-INF” para módulos EJB ou arquivos JAR.

O arquivo beans.xml pode definir a exclusão de filtros. Podemos defini-los através dos elementos <exclude> que são filhos do elemento <scan>. Segue na Listagem 3 um exemplo dessa estrutura.

Listagem 3. Definindo a exclusão de filtros no beans.xml.

  <beans ...>
           <scan>
                     <exclude name="...">
                              . . .
                     </exclude>
           </scan>
  </beans>

Também podemos excluir nomes de pacotes quando uma classe particular for encontrada, como mostra a Listagem 4.

Listagem 4. Excluindo beans no pacote quando encontra uma classe.

  <exclude name="testepkg.beans.*">
           <if-class-available name=" testepkg.beans.TesteSimples"/>
  </exclude>

Neste código, todos os beans no pacote testepkg.beans.* são excluídos se a classe testepkg.beans.TesteSimples for encontrada.

Nomes de pacotes podem ser excluídos se uma classe particular não for encontrada conforme mostra o exemplo da Listagem 5.

Listagem 5. Excluindo beans no pacote se a classe especificada não for encontrada.

  <exclude name="testepkg.beans.*">
           <if-class-not-available name="testepkg.beans.OutraClasseTeste"/>
  </exclude>

Neste código, todos os beans no pacote testepkg.beans.* são excluídos se a classe testepkg.beans.OutraClasseTeste não for encontrada.

Nomes de pacotes podem ser excluídos se uma propriedade do sistema não for definida para um nome particular ou uma propriedade do sistema for definida com um valor em particular. O exemplo da Listagem 6 mostra um exemplo.

Listagem 6. Excluindo beans no pacote se a propriedade do sistema não for definida com certo valor.

  <exclude name="testepkg.beans.*">
           <if-system-property name="minhaPropriedade" value="meuValor"/>
  </exclude>

Neste código, todos os beans no pacote testepkg.beans.* são excluídos se uma propriedade do sistema com o nome minhaPropriedade não for definida com o valor meuValor.

O nome do pacote e todos os subpacotes podem ser excluídos se o nome do filtro terminar com .**, conforme mostra o exemplo da Listagem 7.

Listagem 7. Excluindo beans no pacote e subpacotes se a propriedade do sistema for definida com certo valor.

  <exclude name="testepkg.beans.**">
           <if-system-property name="excluir-beans"/>
  </exclude>

Neste código, todos os beans no pacote testepkg.beans.* e subpacotes são excluídos se uma propriedade do sistema com o nome excluir-beans for setado.

O CDI 1.1 introduz também uma nova anotação, a anotação @Vetoed, na qual podemos prevenir um bean de injeção de dependência. O código da Listagem 8 mostra como isso pode ser feito.

Listagem 8. Prevenindo um bean de injeção de dependência.

  @Vetoed
  public class TesteSimplesImpl implements TesteSimples {
           ...
  }

Neste código o bean TesteSimplesImpl não será considerado para injeção.

Todos os beans no pacote podem ser prevenidos de injeção como mostra o código da Listagem 9.

Listagem 9. Prevenindo todos os beans de injeção de dependência.

  @Vetoed
  package testepkg.beans;
  import javax.enterprise.inject.Vetoed;

Neste código no pacote testepkg.beans prevenirá todos beans deste pacote.

Componentes Java EE como os EJBs sem estado de sessão ou recursos JAX-RS (endpoints), podem ser marcados com @Vetoed para preveni-los de serem considerados para injeção.

Pontos de Injeção

Podemos injetar um bean em um campo, método, ou construtor usando @Inject.

O seguinte código da Listagem 10 mostra uma interface TesteInterface, um POJO TesteImpl (Listagem 11), e a injeção da interface como um campo no EJB TesteService (Listagem 12).

Listagem 10. Exemplo de uma interface.

  public interface TesteInterface {
           public String msgboasvindas(String name);
  }
Listagem 11. Exemplo de implementação da interface.

  public class TesteImpl implements TesteInterface {
           public String msgboasvindas(String name) {
                     return "Olá" + name;
           }
  }
Listagem 12. Injeção da interface como um campo no EJB.

  @Stateless
  public class TesteService {
           @Inject TesteInterface boasvindas;
           public String msgboasvindas(String name) {
                     return boasvindas.msgboasvindas (name);
           }
  }

A anotação @Inject especifica o ponto de injeção, TesteInterface especifica o que precisa para ser injetado e "boasvindas" é a variável que recebe a injeção.

Um bean pode definir um ou mais métodos como alvos de injeção, conforme mostra a Listagem 13.

Listagem 13. Definindo um método como alvo de injeção.

  TesteInterface boasvindas;
  @Inject
  public setBoasVindas(TesteInterface boasvindas) {
           this.boasvindas = boasvindas;
  }

Finalmente, um bean pode ter no máximo um construtor marcado com @Inject, conforme a Listagem 14.

Listagem 14. Definindo um construtor como alvo de injeção.

  TesteInterface boasvindas;
  @Inject
  public TesteImpl(TesteInterface boasvindas) {
           this. boasvindas = boasvindas;
  }

Todos os parâmetros do método são então automaticamente injetados. Este construtor pode ter qualquer número de parâmetros, e todos eles são pontos de injeção. Além disso, um construtor marcado com @Inject não precisa ter acesso público.

A sequencia de iniciação de um bean primeiramente se inicia com o construtor padrão ou um construtor anotado com @Inject, após isso todos os campos do bean anotado com @Inject, na sequencia todos os métodos do bean anotados com @Inject e por fim o método @PostConstruct se houver algum.

Qualificadores e Alternativas

Qualificadores permitem especificar exclusivamente um bean a ser injetado entre suas várias implementações.

Por exemplo, este código declara um novo qualificador, @TesteQualificador conforme mostra o código da Listagem 15.

Listagem 15. Declarando um novo qualificador.

  @Qualifier
  @Retention(RUNTIME)
  @Target({METHOD, FIELD, PARAMETER, TYPE})
  public @interface TesteQualificador {
  }

Na Listagem 16 a definição de uma nova implementação da interface TesteInterface definida anteriormente.

Listagem 16. Nova implementação da interface TesteInterface.

  @TesteQualificador
  public class TesteQualificadorImpl implements TesteInterface {
           public String msgboasvindas(String name) {
                     return "Prazer em conhecer você " + name;
           }
  }

Após isso ela é injetada em TesteService especificando @TesteQualificador como qualificador, assim como mostra a Listagem 17.

Listagem 17. Injetando o qualificador no serviço.

  @Stateless
  public class TesteService {
   
           @Inject @TesteQualificador TesteInterface boasvindas;
   
           public String digaOla(String name) {
                     return boasvindas.msgboasvindas(name);
           }
  }

Este código remove qualquer dependência direta com qualquer implementação particular da interface. Também podemos especificar múltiplos qualificadores em um ponto de injeção.

Segue abaixo a lista de qualificadores embutidos e seus significados:

  • @Named: Qualificador baseado em String, é necessário para uso em Expression Language;
  • @Default: Qualificador padrão em todos os beans sem um qualificador explícito, exceto @Named;
  • @Any: Qualificador padrão em todos os beans exceto @New;
  • @New: Permite que a aplicação obtenha uma nova instância independente do escopo declarado.

Usando as implementações TesteImpl e TesteQualificadorImpl definidas anteriormente, os pontos de injeção são exemplificados na Listagem 18.

Listagem 18. Exemplificando os pontos de injeção.

  @Inject TesteInterface boasvindas;
  @Inject @Default TesteInterface boasvindas;
  @Inject @Any @Default TesteInterface boasvindas;

Os três pontos de injeção acima são equivalentes, como cada bean tem os qualificadores @Default e @Any (exceto @New) então não são necessários especificá-los.

O bean TesteImpl é injetado em cada comando. Assim, o seguinte código:

@Inject @Any @TesteQualificador TesteInterface boasvindas;

injetará a implementação TesteQualificadorImpl. Isto ocorre porque especificar @TesteQualificador em TesteQualificadorImpl significa que ele não tem o qualificador @Default.

O seguinte comando:

@Inject @Any TesteInterface boasvindas;

irá resultar em dependência ambígua e nos obriga a qualificar ainda mais o bean, especificando @Default ou @TesteQualificador.

O uso de @Named como um qualificador de ponto de injeção não é recomendado, exceto no caso de integração com código legado que usa nomes baseados em String para identificar os beans.

Os beans marcados com @Alternative estão indisponíveis para injeção, lookup ou EL. Precisamos explicitamente habilitá-los no beans.xml usando , como mostra a Listagem 19.

Listagem 19. Utilizando @Alternative para indisponibilizar os beans.

  @Alternative
  public class TesteImpl implements TesteInterface {
           //. . .
  }
   
  @TesteQualificador @Alternative
  public class TesteQualificadorImpl implements TesteInterface {
           //. . .
  }

Porém, a seguinte injeção dará um erro de "dependência não resolvida":

@Inject TesteInterface boasvindas;

Isso ocorrerá porque ambos os beans estão desativados para injeção.

Para resolver este erro podemos explicitamente ativar um dos beans no beans.xml (Listagem 20).

Listagem 20. Ativar beans

  <beans
           xmlns="http://xmlns.jcp.org/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
           http://xmlns.jcp.org/xml/ns/javaee
           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
           bean-discovery-mode="annotated">
   
           <alternatives>
                     <class>testepkg.TesteQualificadorImpl</class>
           </alternatives>
   
  </beans>

Produtores

Um método produtor gera um objeto que podem ser injetado. Os métodos produtores são usados quando queremos injetar um objeto que não é propriamente um bean, quando o tipo concreto do objeto a ser injetado pode variar em tempo de execução ou quando o objeto requer alguma customização na inicialização que o construtor do bean não pode executar.

Um campo produtor é uma alternativa mais simples a um método produtor. Este é um campo de um bean que gera um objeto e pode ser usado ao invés de um simples método getter. Campos produtores são úteis quando desejamos declarar recursos Java EE como data sources, recursos, JMS e referências a web service.

Um método produtor ou campo é anotado com javax.enterprise.inject.Produces.

O método produtor nos permite selecionar a implementação de um bean em tempo de execução, ao invés de em tempo de desenvolvimento ou de implantação.

Interceptadores

Interceptadores são utilizados para implementar logging, auditoria e segurança na camada lógica de negócio.

A especificação não é inteiramente nova, visto que o conceito já existia na especificação do EJB 3.0. No entanto, agora a especificação é abstraída a um nível mais elevado de modo que ela possa ser aplicada mais genericamente para um amplo conjunto de especificações da plataforma J2EE.

Interceptadores fazem exatamente o que nome sugere, ou seja, interceptar invocações e eventos do ciclo de vida em uma classe de destino associada.

Basicamente um interceptador é uma classe na qual os métodos são invocados quando métodos de negócio em uma classe de destino são chamados, ou quando eventos do ciclo de vida como create/destroy do bean ocorrem, ou ainda quando um método timeout de um EJB ocorre.

A especificação CDI define um mecanismo de tipo seguro (type-safe) para associar interceptadores de beans usando um InterceptorBinding.

Precisamos definir um interceptador a fim de interceptar um método de negócio.

Podemos fazer isto especificando a anotação @InterceptorBinding conforme mostra o código da Listagem 21.

Listagem 21. Definindo um interceptor.

  @InterceptorBinding
  @Retention(RUNTIME)
  @Target({METHOD,TYPE})
  public @interface Logging {
   
  }

A anotação @Target define o elemento do programa para que este possa ser aplicado ao interceptador. Neste caso, a anotação @Logging pode ser aplicada para um método (METHOD) ou um tipo (TYPE) podendo ser uma classe, interface, ou enumerador.

O interceptador é implementado como segue na Listagem 22.

Listagem 22. Implementação do interceptor.

  @Interceptor
  @Logging
  public class LoggingInterceptor {
  @AroundInvoke
           public Object log(InvocationContext context) throws Exception {
                     String name = context.getMethod().getName();
                     String params = context.getParameters().toString();
                     //. . .
                     return context.proceed();
           }
  }

Adicionando a anotação @Interceptor marcamos esta classe como uma interceptadora, e @Logging especifica que esta é uma implementação do tipo Interceptor conforme definido anteriormente. A anotação @AroundInvoke indica que este método do interceptador sobrepõe métodos de negócio. Apenas um método de um interceptador pode ser marcado com esta anotação. InvocationContext fornece informações de contexto como invocações interceptadas e operações, além disso, pode ser usado para controlar o comportamento da cadeia de chamada. Através do contexto também podemos recuperar o nome do método de negócio invocado e os parâmetros passados.

Este interceptor pode ser anexado a qualquer Managed Bean. Segue um exemplo na Listagem 23.

Listagem 23. Inserindo o interceptor em um Managed Bean.

  @Logging
  public class TesteImpl {
           //. . .
  }

Como alternativa, pode-se logar métodos individuais, anexando o interceptor conforme mostra a Listagem 24.

Listagem 24. Inserindo o interceptor em um método específico.

  public class TesteImpl {
           @Logging
           public String msgboasvindas(String name) {
                     //. . .
           }
  }

Podemos definir múltiplos interceptadores usando o mesmo InterceptorBinding.

Por padrão, todos interceptadores estão desabilitados. Eles precisam ser explicitamente habilitados e ordenados através da anotação @Priority, juntamente com um valor de prioridade na classe interceptadora. Segue na Listagem 25 um exemplo.

Listagem 25. Definindo uma prioridade para o interceptor.

  @Priority(Interceptor.Priority.APPLICATION+10)
  @Interceptor
  @Logging
  public class LoggingInterceptor {
           //. . .
  }

Os valores das prioridades são definidos na classe javax.interceptor.Interceptor.Priority conforme especificado na Tabela 1.

Valor da Prioridade

Valor da constante

Interceptor.Priority.PLATFORM_BEFORE

0

Interceptor.Priority.LIBRARY_BEFORE

1000

Interceptor.Priority.APPLICATION

2000

Interceptor.Priority.LIBRARY_AFTER

3000

Interceptor.Priority.PLATFORM_AFTER

4000

Tabela 1. Definição das constantes e do valor de cada uma delas.

Interceptores com os valores de prioridade menores são chamados em primeiro lugar. Se mais que um interceptador tem a mesma prioridade, a ordem relativa destes interceptores é indefinida.

Normalmente temos que Interceptadores definidos pela plataforma Java EE que são executados no inicio da cadeia de interceptadores executam com prioridade maior quePLATFORM_BEFORE e menor queLIBRARY_BEFORE; Interceptores definidos pelas bibliotecas de extensão que se destinam a serem executados no início da cadeia de interceptores possuem prioridade maior queLIBRARY_BEFORE e menor que APPLICATION; Interceptadores definidos por aplicações possuem prioridade maior queAPPLICATION e menor que LIBRARY_AFTER; Interceptores definidos por bibliotecas de extensão que se destinam a serem executados mais tarde na cadeia interceptores possuem prioridade maior que LIBRARY_AFTER e menor que PLATFORM_AFTER; Interceptadores definidos pela plataforma Java EE que são executados no final da cadeia de interceptadores executam com prioridade maior que PLATFORM_AFTER.

O LoggingInterceptor definido anteriormente é executado depois que todos interceptadores específicos de aplicação são chamados, mas antes dos interceptadores definidos por bibliotecas de extensão.

Podemos também explicitamente habilitar interceptadores através do arquivo beans.xml, conforme mostra a Listagem 26.

Listagem 26. Habilitar interceptadores

  <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
           http://xmlns.jcp.org/xml/ns/javaee
           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
           bean-discovery-mode="annotated">
   
           <interceptors>
                     <class>testepkg.LoggingInterceptor</class>
           </interceptors>
  </beans>

Podemos notar que a classe de implementação do interceptador está definida acima.

Os interceptores são chamados na ordem em que são especificados dentro do elemento .

Um interceptador também suporta injeção de dependência. Um interceptador que adiciona comportamento transacional para um Managed Bean pode ser definido conforme o código da Listagem 27.

Listagem 27. Definindo um comportamento transacional para um bean.

  @Interceptor
  @MeuTransactional
  public class MeuTransactionInterceptor {
           @Resource UserTransaction tx;
           @AroundInvoke
           public Object gerenciaTransaction(InvocationContext context) {
                     tx.begin();
                     //. . .
                     Object response = context.proceed();
                     //. . .
                     tx.commit();
                     return response;
           }
  }

UserTransaction é injetado no interceptador e então é usado para iniciar e confirmar (commit) a transação no método interceptador. @MeuTransactional pode agora ser especificado em qualquer Managed Bean ou um método thereof para indicar o comportamento transacional.

A Java Transaction API introduz uma nova anotação @javax.transaction.Transactional que habilita uma aplicação a controlar declarativamente as fronteiras de uma transação em beans gerenciados por CDI, assim como classes definidas como Managed Bean, Servlets, JAXRS e JAX-WS.

Métodos do ciclo de vida de um interceptor pode ser implementado, conforme mostra a Listagem 28.

Listagem 28. Definindo um método do ciclo de vida de um interceptor.

  public class CicloDeVidaInterceptor {
           @PostConstruct
           public void init(InvocationContext context) {
                     //. . .
           }
  }

Um interceptador de timeout pode ser implementado como mostra a Listagem 29.

Listagem 29. Definindo um interceptor para timeout.

  public class TimeoutInterceptor {
           @AroundTimeout
           public Object timeout(InvocationContext context) {
                     //. . .
           }
  }

Decorators

Decorators são usados para implementar conceitos de negócio. Interceptadores por sua vez desconhecem a semântica de negócios do bean chamado e, portanto são amplamente aplicáveis. Por isso que os Decorators complementam os interceptadores, visto que eles têm conhecimento sobre as semânticas do negócio e são aplicáveis a beans de um tipo particular.

Um Decorator é um bean que implementa o bean que o decora, e é anotado com @Decorator, conforme demonstra o exemplo da Listagem 30.

Listagem 30. Definindo um Decorator.

  @Decorator
  class MeuDecorator implements TesteInterface {
           public String msgboasvindas(String name) {
                     //. . .
           }
  }

A classe Decorator pode ser abstrata uma vez que não pode implementar todos os métodos do bean. Uma classe Decorador tem um ponto de injeção delegado que é um ponto de injeção para o mesmo tipo que o bean decora.

O ponto de injeção delegado segue as regras normais para injeção e, portanto deve ser injetado em um campo, parâmetro de um método inicializador ou parâmetro de um método construtor de um bean conforme abaixo:

@Inject @Delegate @Any TesteInterface boasvindas;

Este ponto de injeção delegado especifica que o Decorator está vinculado a todos os beans que implementam TesteInterface. Um ponto de injeção delegado pode especificar qualificadores, e o Decorator é então vinculado aos beans com os mesmos qualificadores.

Por padrão, todos os Decorators estão desabilitados e precisam ser explicitamente habilitados e ordenados através da anotação @Priority, juntamente com um valor de prioridade na classe Decorator conforme a Listagem 31.

Listagem 31. Definindo a prioridade de um Decorator.

  @Decorator
  @Priority(Interceptor.Priority.APPLICATION+10)
  class MyDecorator implements Greeting {
           public String greet(String name) {
                     //. . .
           }
  }

Os valores de prioridade são definidos na classe javax.interceptor.Interceptor.Priority conforme explicitado anteriormente. Decorators com valores de prioridade menores são chamados primeiro.

Um Decorator habilitado através da anotação @Priority está habilitado para a aplicação inteira. Assim como interceptadores, também podemos especificar um conjunto de Decorators com base no ambiente de implantação.

Podemos habilitar um Decorator especificando ele no arquivo beans.xml conforme mostra o código da Listagem 32.

Listagem 32. Habilitando Decorators no beans.xml.

  <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="
           http://xmlns.jcp.org/xml/ns/javaee
           http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
           bean-discovery-mode="annotated">
   
           <decorators>
                     <class>testepkg.MeuDecorator</class>
           </decorators>
  </beans>

Decorators habilitados através de @Priority são chamados antes dos Decorators habilitados através de beans.xml.

Em ordem de execução, os interceptadores para um método são chamados antes dos Decorators que se aplicam ao método.

Escopo e Contexto

Um bean está em um escopo e associado com um contexto. O contexto associado gerencia o ciclo de vida e a visibilidade de todos os beans naquele escopo.

Um bean é criado uma vez por escopo e então reutilizado. Quando um bean é solicitado em um escopo particular, uma nova instância é criada se ele ainda não existe. Se ele já existe, essa instância é retornada.

Existem quatro escopos pré-definidos e um default, são eles: @RequestScoped onde um bean tem um escopo para requisições e o bean está disponível durante uma simples solicitação e destruído quando a requisição está completa; @SessionScoped onde um bean tem um escopo para uma sessão e o bean é compartilhado entre todas as requisições que ocorrem na mesma sessão HTTP, mantém o estado durante toda a sessão, e é destruída quando a sessão HTTP expira ou é invalidada; @ApplicationScoped onde um bean tem um escopo para uma aplicação e o bean é criado quando a aplicação é iniciada, mantém o estado durante toda a aplicação e é destruída quando a aplicação é encerrada; @ConversationScoped onde um bean tem um escopo para conversação e possui dois tipos: transient e long-running. Por padrão um bean possui escopo transient, é criado com uma solicitação JSF e é destruída no final da solicitação. No entanto, uma conversação transient pode ser convertida para uma conversação long-running através de um Conversation.begin e pode ser encerrada através de uma Conversation.end. Todas as conversações long-running estão com um escopo de sessão HTTP Servlet e pode ser propagada para outras requisições JSF; Por fim @Dependent é onde um bean pertence a um pseudo escopo dependente, este é o escopo padrão do bean e não precisa ser explicitamente declarado.

Estereótipos

Um estereótipo encapsula um padrão arquitetural ou metadados comuns para beans que produzem roles (papéis) recorrentes em um lugar central. Este encapsula escopo, interceptadores, qualificadores e outras propriedades dos roles.

Um estereótipo é uma meta-anotação anotada com @Stereotype conforme Listagem 33.

Listagem 33. Criando um estereótipo utilizando a anotação @Stereotype

  @Stereotype
  @Retention(RUNTIME)
  @Target(TYPE)
  //. . .
  public @interface MeuEstereotipo { }
  

Um estereótipo que adiciona comportamento transacional pode ser definido como o exemplo da Listagem 34.

Listagem 34. Criando um estereótipo contendo comportamento transacional.

  @Stereotype
  @Retention(RUNTIME)
  @Target(TYPE)
  @Transactional
  public @interface MeuEstereotipo { }

Neste código, um interceptador definido anteriormente, @Transactional, é usado para definir o estereótipo.

Um estereótipo pode ser especificado no bean de destino como qualquer outra anotação (Listagem 35).

Listagem 35. Especificando um estereótipo no bean.

  @MyStereotype
  public class MeuBean {
           //. . .
  }

Os metadados definidos pelo estereótipo são agora aplicáveis no bean.

Um estereótipo pode declarar o escopo padrão de um bean conforme Listagem 36.

Listagem 36. Declarando o escopo padrão no estereótipo.

  @Stereotype
  @RequestScoped
  @Retention(RUNTIME)
  @Target(TYPE)
  public @interface MeuEstereotipo { }

A especificação desse estereótipo em um bean faz com que o bean tenha um escopo @RequestScoped a menos que o bean especifique explicitamente o escopo. Um estereótipo pode declarar, no máximo, um escopo. Além disso, um estereótipo pode declarar zero, um, ou vários interceptadores. Segue um exemplo da Listagem 37.

Listagem 37. Especificando um interceptor no estereótipo.

  @Stereotype
  @Transactional
  @Logging
  @Retention(RUNTIME)
  @Target(TYPE)
  public @interface MyStereotype { }

Estereótipos podem ser “embrulhados” juntos para criar novos estereótipos também.

Também existem alguns estereótipos pré-definidos como @Interceptor, @Decorator, e @Model. O estereótipo @Model é predefinido conforme Listagem 38.

Listagem 38. Estereótipo Model pré-definido.

  @Named
  @RequestScoped
  @Stereotype
  @Target({TYPE, METHOD})
  @Retention(RUNTIME)
  public @interface Model {}

Este estereótipo fornece um nome padrão para o bean e o marca como @RequestScoped.

Eventos

Os eventos fornecem um modelo de evento baseado em anotação que atua em cima do padrão Observer. Produtores de Eventos disparam eventos que são consumidos pelos Observadores. O objeto de evento, normalmente um POJO, carrega o estado do produtor ao consumidor. O produtor e o observador são completamente dissociados um do outro e só se comunicam usando o estado.

Um bean produtor irá disparar um evento usando a interface Event (Listagem 39).

Listagem 39. Produtor disparando eventos através da interface padrão.

  @Inject @Any Event<Cliente> event;
  //. . .
  event.fire(cliente);

Um bean observador que tenha um método com a seguinte assinatura irá receber o evento (Listagem 40).

Listagem 40. Observador recebendo o evento transmitido.

  void noCliente(@Observes Cliente event) {
           //. . .
  }

Neste código Cliente está carregando o estado do evento.

O bean produtor pode especificar um conjunto de qualificadores quando injetamos o evento:

@Inject @Any @Added Event<Cliente> event;

A assinatura do método do bean observador precisa combinar exatamente com o conjunto de qualificadores de forma a receber os eventos disparados por este bean como abaixo:


  void noCliente(@Observes @Added Cliente event) {
           //. . .
  }

Por padrão, uma instância existente do bean ou uma nova instância do bean é criada no contexto atual para entregar o evento. Este comportamento pode ser alterado para que o evento seja entregue apenas se o bean já existe no escopo atual, segue na Listagem 41 um exemplo de como poderíamos fazer isso.

Listagem 41. Exemplo de como entregar o evento apenas quando o bean já existe no escopo.

  void onCustomer(@Observes(notifyObserver= Reception.IF_EXISTS) @Added Customer event){
           //. . .
  }

Métodos observadores transacionais recebem notificações de eventos durante as fases anterior ou posterior da conclusão da transação em que o evento foi disparado.

O TransactionPhase identifica o tipo dos métodos observadores transacionais, como definido abaixo:

  • IN_PROGRESS: comportamento padrão onde os observadores são chamados imediatamente.
  • BEFORE_COMPLETION: observadores são chamados anteriormente à conclusão da transação.
  • AFTER_COMPLETION: observadores são chamados após a conclusão da transação.
  • AFTER_FAILURE: observadores são chamados após a conclusão da transação, apenas quando a transação falha.
  • AFTER_SUCCESS: observadores são chamados após a conclusão da transação, apenas quando a transação é bem sucedida.

Por exemplo, o seguinte método (Listagem 42) observador será chamado depois que a transação foi concluída com êxito:

Listagem 42. Observador chamado depois que a transação é concluída.

  void noCliente(@Observes(during= TransactionPhase.AFTER_SUCCESS) @Added Cliente event) {
           //. . .
  }

Um evento não é acionado para qualquer bean anotado com @Vetoed, ou em um pacote anotado com @Vetoed.

Extensões Portáveis

O CDI expõe um Service Provider Interface (SPI) que permite extensões portáveis para estender a funcionalidade do container com bastante facilidade.

Uma extensão portátil pode se integrar com o container fornecendo seus próprios beans, interceptores e decoradores, injetando dependências em seus próprios objetos usando o serviço de injeção de dependência, fornecendo uma implementação contextual para um escopo customizado, aumentando ou substituindo os metadados baseados em anotações com metadados de alguma outra fonte.

Segue na Listagem 43 uma simples extensão.

Listagem 43. Definindo uma extensão para o container.

  public class MinhaExtensao implements Extension {
           <T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {
                     Logger.global.log(Level.INFO,"processando a anotação: {0}",pat.getAnnotatedType().getJavaClass().getName());
           }
  }

Esta extensão imprime a lista de anotações em um bean empacotado em uma aplicação web; A extensão precisa implementar a interface Extension. Então precisamos registrar esta extensão usando o serviço de mecanismo do provedor criando um arquivo com o nome META-INF/services/javax.enterprise.inject.spi.Extension. Este arquivo contém o nome completo da classe: testepkg.MinhaExtensao.

O bean pode escutar uma variedade de eventos do ciclo de vida do container, são eles: BeforeBeanDiscovery que é disparado antes do processo de descoberta do bean iniciar; AfterBeanDiscovery que é disparado após o processo de descoberta ser completa; AfterDeploymentValidation que é disparado depois que nenhum problema na implantação foi encontrado, antes do contexto ser criado e as solicitações serem processadas; BeforeShutdown que é disparado depois que o processamento de todas as solicitações são finalizadas e todos os contextos destruídos; ProcessAnnotatedType é disparado para cada classe Java ou interface descoberta na aplicação, antes das anotações serem lidas; ProcessInjectionTarget é disparado para toda classe que suporte injeção de dependência; e por fim, ProcessProducer para cada método produtor ou campo de cada bean habilitado.

Cada um desses eventos permite uma extensão portável para integrar com a inicialização do container.

Por exemplo, BeforeBeanDiscovery pode ser usado para adicionar novos interceptadores, qualificadores, escopo e estereótipos em um bean existente.

Bibliografia

  • [1] G. Arun, Java EE 7 Essentials. O’Reilly, 2013.
  • [2] The Java EE 7 Tutorial - Overview of CDI, disponível em http://docs.oracle.com/javaee/7/tutorial/doc/cdi-basic002.htm