Injeção de Dependência no Java EE
Veja neste artigo o que é a injeção de dependência que está especificada na plataforma Java EE 7. Também veremos diversos exemplos de como a injeção de dependência é tratada na plataforma Java.
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.
<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.
<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.
<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.
<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.
<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.
<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.
<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.
@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.
@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).
public interface TesteInterface {
public String msgboasvindas(String name);
}
public class TesteImpl implements TesteInterface {
public String msgboasvindas(String name) {
return "Olá" + name;
}
}
@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.
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.
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.
@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.
@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.
@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.
@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.
@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).
<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.
@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.
@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.
@Logging
public class TesteImpl {
//. . .
}
Como alternativa, pode-se logar métodos individuais, anexando o interceptor conforme mostra a Listagem 24.
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.
@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 |
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.
<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.
@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.
public class CicloDeVidaInterceptor {
@PostConstruct
public void init(InvocationContext context) {
//. . .
}
}
Um interceptador de timeout pode ser implementado como mostra a Listagem 29.
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.
@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.
@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.
<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.
@Stereotype
@Retention(RUNTIME)
@Target(TYPE)
//. . .
public @interface MeuEstereotipo { }
Um estereótipo que adiciona comportamento transacional pode ser definido como o exemplo da Listagem 34.
@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).
@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.
@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.
@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.
@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).
@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).
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.
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:
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.
public class MinhaExtensao implements Extension {
<T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> pat) {
Logger.global.log(Level.INFO,"processando a anotação: ",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
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Vídeo