A injeção de dependência e contextos (CDI), especificada por JSR-299, é uma parte integral do Java EE 6 e fornece uma arquitetura que permite que os componentes Java EE, como os servlets, enterprise beans e JavaBeans, existam dentro do ciclo de vida de um aplicativo com escopos bem definidos. Além disso, o serviço CDI permite que os componentes do Java EE, como beans de sessão EJB e beans gerenciados do JavaServer Faces (JSF) sejam injetados e interajam de maneira acoplada flexível, disparando e observando eventos.
Este tutorial é baseado no post do blog de Andy Gibson, intitulado Primeiros passos com o CDI parte 3: Eventos. Ele demonstra como obter benefícios do conceito do Java EE de eventos, em que você produz e se inscreve (por exemplo, observar), eventos que ocorrem no seu aplicativo de maneira que o permite manter o código desacoplado entre produtores e observadores. A classe javax.enterprise.event.Event é utilizada para criar eventos e anotação @Observes de CDI para se inscrever em eventos.
O NetBeans IDE fornece um suporte embutido para a Injeção de dependência e contextos, incluindo a opção de geração do arquivo de configuração CDI beans.xml durante a criação do projeto, do editor e do suporte de navegação para anotações, assim como vários assistentes para a criação de artefatos CDI comumente utilizados.
Para concluir este tutorial, os seguintes recursos e softwares são necessários.
O conjunto NetBeans IDE Java também inclui o servidor de aplicativos GlassFish Server Open Source Edition 3.x, que é um contêiner em conformidade com o Java EE 6.
O suporte para CDI também está disponível para o NetBeans 6.8 através do Patch 1.
O projeto de amostra de solução para este tutorial pode ser baixado: cdiDemoComplete.zip
Utilizando eventos
No tutorial anterior, Aplicação de anotações de @Alternative Beans e de ciclo de vida, tínhamos um aplicativo que obtinha uma lista de itens, validava-os e agia, de maneira específica, quando um item inválido era encontrado. Vamos dizer que no futuro queremos expandir nosso sistema para manipular todos os tipos de coisas que ocorrem quando encontramos um item inválido. Isso pode variar de um e-mail que é enviado, alterações feitas a outros dados, como um pedido sendo cancelado ou o armazenamento de uma lista de rejeições em um arquivo ou tabela de banco de dados. Para desacoplar completamente a implementação, podemos utilizar eventos no Java EE. Eventos são gerados pelo produtor de eventos e inscritos pelos observadores de eventos. Como a maior parte do CDI, a produção e inscrição de eventos é segura para digitação e permite que os qualificadores determinem quais eventos os observadores irão observar.
Utilizando o aplicativo que estamos construindo desde os tutoriais anteriores da série, não precisamos de muitas alterações para implementar isso. Podemos fornecer apenas outra implementação de ItemErrorHandler (criado no tutorial anterior), que gera um evento toda vez que manipula um item. Nomearemos essa classe EventItemHandler, a injetaremos no ItemProcessor e utilizaremos um qualificador Notifypara selecioná-la para injeção.
Comece extraindo o projeto de início de amostra do arquivo cdiDemo3.zip (Consulte a tabela que lista os recursos necessários acima.) Abra o projeto no IDE escolhendo Arquivo > Abrir projeto (Ctrl-Shift-O; ⌘-Shift-O no Mac) e, em seguida, selecione o projeto no seu local no computador.
Crie uma classe nomeada EventItemHandler. Clique em Novo arquivo ( ) ou pressione Crtl-N (⌘-N no Mac) para abrir o assistente Arquivo.
Selecione a categoria Java e, em seguida, a classe Java. Clique em Próximo.
Digite em EventItemHandler como o nome da classe e, em seguida, insira exercise4 como o pacote.
Clique em Terminar. A nova classe e o novo pacote são gerados e a classe é aberta no editor.
Implemente EventItemHandler como se segue.
public class EventItemHandler implements ItemErrorHandler {
@Inject
private Event<Item> itemEvent;
@Override
public void handleItem(Item item) {
System.out.println("Firing Event");
itemEvent.fire(item);
}
}
Injetamos uma instância de um Evento em que a carga do evento será um Item. A carga do evento são os dados de estado passados do produtor de evento para o observador de evento que, nesse caso, passa o item rejeitado. Quando o item inválido é manipulado, nós acionamos o evento e passamos no item inválido que recebemos. Esse manipulador de item com base em evento é injetado da mesma maneira que qualquer outro manipulador de item seria e, portanto, podemos colocá-lo ou tirá-lo sempre que precisarmos e também podemos substituí-lo durante os testes.
Corrigir todas as importações. Clique com o botão direito do mouse no editor e selecione Corrigir importações ou pressione Ctrl-Shift-I (⌘-Shift-I no Mac). Certifique-se de selecionar javax.enterprise.event.Event como o nome completamente qualificado para a classe Evento.
Pressione Ctrl-Espaço em Evento para visualizar a definição Javadoc da classe. O método fire(), utilizado acima, também é definido.
@Notify
public class EventItemHandler implements ItemErrorHandler {
...
}
Criamos um qualificador @Notify para identificar esse manipulador de erros para injeção e podemos utilizá-lo em nosso ItemProcessor adicionando-o ao ponto de injeção.
Adicione a anotação @Notify ao ponto de injeção do EventItemHandler no exercise2.ItemProcessor.
@Named
@RequestScoped
public class ItemProcessor {
@Inject @Demo
private ItemDao itemDao;
@Inject
private ItemValidator itemValidator;
@Inject @Notify
private ItemErrorHandler itemErrorHandler;
public void execute() {
List<Item> items = itemDao.fetchItems();
for (Item item : items) {
if (!itemValidator.isValid(item)) {
itemErrorHandler.handleItem(item);
}
}
}
}
(Utilize a dica do editor para adicionar a instrução de importação para exercise4.Notify.)
Clique no botão Executar projeto ( ) para executar o projeto.
No navegador, clique no botão "Executar" e, em seguida, retorne para o IDE e examine o registro do servidor na janela Saída (Ctrl-4; ⌘-4 no Mac). Como o aplicativo que você tem construído atualmente utiliza o DefaultItemDao para configurar quatro Itens, e, em seguida, aplica o RelaxedItemValidator nos Itens, é esperado ver o acionamento itemErrorHandler duas vezes.
Atualmente, no entanto, não temos nada observando o evento. Podemos corrigir isso criando um método observador utilizando a anotação @Observes. Essa é a única coisa necessária para observar um evento. Para demonstrar, podemos modificar o FileErrorReporter (criado no tutorial anterior) para responder a eventos acionados adicionando um método observador que chama seu método handleItem().
Para fazer nosso FileErrorReporter responder ao evento, adicione o seguinte método à classe.
public class FileErrorReporter implements ItemErrorHandler {
public void eventFired(@Observes Item item) {
handleItem(item);
}
...
}
(Utilize a dica do editor para adicionar uma instrução de importação para javax.enterprise.event.Observes.)
Execute o projeto (F6; fn-F6 no Mac) novamente, clique no botão "Executar", retorne para o IDE e examine o registro do servidor na janela Saída.
Você verá que os eventos são acionados em objetos inválidos como eram anteriormente, mas agora as informações do item estão sendo salvas quando cada evento é acionado. Também é possível notar que os eventos de ciclo de vida estão sendo observados, já que um bean FileErrorReporter é criado e fechado para cada evento acionado. (Consulte Aplicando anotações @Alternative Beans e de ciclo de vida para obter uma discussão de anotações de ciclo de vida, como @PostConstruct e @PreDestroy.)
Conforme mostrado nas etapas acima, a anotação @Observes fornece uma maneira fácil de observar um evento.
Eventos e observadores também podem ser anotados com qualificadores para habilitar os observadores a observar apenas eventos específicos de um item. Consulte Primeiros passos com CDI parte 3: Eventos para obter uma demonstração.
Manipulando escopos
No estado atual do aplicativo, um bean FileErrorReporter é criado toda vez que o evento é criado. Nesse caso, não queremos criar um novo bean toda vez que abrir e fechar o arquivo para cada item não for desejado. Ainda queremos abrir o arquivo no início do processo e, em seguida, fechá-lo depois que o processo tiver sido concluído. Portanto, precisamos considerar o escopo do bean FileErrorReporter.
Atualmente, o bean FileErrorReporter não tem um escopo definido. Quando nenhum escopo é definido, o CDI utiliza o escopo pseudodependente padrão. Na prática, isso significa que o bean é criado e destruído em um espaço muito pequeno de tempo, normalmente em um chamado de método. No nosso cenário atual, o bean é criado e destruído pela duração do evento sendo acionado. Para corrigir isso, podemos aumentar o escopo do bean adicionando manualmente uma anotação de escopo. Tornaremos esse bean @RequestScoped de modo que quando o bean for criado com o primeiro evento sendo acionado, ele continuará a existir pela duração da requisição. Isso também significa que para todos os pontos de injeção, nos quais esse bean é qualificado para ser injetado, a mesma instância do bean será injetada.
Adicione a anotação @RequestScope e a instrução de importação correspondente para javax.enterprise.context.RequestScoped à classe FileErrorReporter.
import javax.enterprise.context.RequestScoped;
...
@RequestScoped
public class FileErrorReporter implements ItemErrorHandler { ... }
Para invocar o suporte de autocompletar de código do editor, pressione Ctrl-Espaço enquanto digita. Ao escolher um item por meio do autocompletar de código, todas as instruções de importação associadas serão automaticamente adicionadas à classe. menu suspenso do autocompletar de código no editor"
alt="
Execute o projeto (F6; fn-F6 no Mac) novamente, clique no botão "Executar" e, em seguida, retorne para o IDE e examine o registro do servidor na janela Saída.
Note que o bean FileErrorReporter é criado apenas quando o primeiro evento é acionado e fechado depois de o evento final tiver sido acionado.
Os eventos são uma ótima maneira de desacoplar partes do sistema de maneira modular, já que os observadores e produtores de eventos não sabem nada um sobre o outro, nem exigem alguma configuração para isso. É possível adicionar partes de códigos que se inscrevem a eventos com o produtor de evento desconhecendo o observador. (Sem utilizar eventos, normalmente seria necessário fazer com que o produtor de eventos chamasse o observador manualmente.) Por exemplo, se alguém atualizar o status de um pedido, seria possível adicionar eventos por e-mail para o representante de vendas ou notificar um gerente de conta se um problema do suporte técnico estiver aberto há mais de uma semana. Esses tipos de regras podem ser implementados sem eventos, mas os eventos facilitam o desacoplamento da lógica de negócio. Além disso, não há nenhuma dependência de compilação ou de tempo de construção. É possível apenas adicionar módulos a seus aplicativos e eles começarão, automaticamente, observando e produzindo eventos.