Java EE 6 e o CDI – Contexts and Dependency Injection

Trata do padrão CDI – Contexts and Dependency Injection for the JavaTM Platform –, proposto pela JSR 299 e lançado em dezembro de 2009 como parte da plataforma Java EE, versão 6. Por ser uma das tecnologias incluídas nesta plataforma, está disponível para utilização por qualquer servidor Web ou servidor de aplicação Java EE 6, como por exemplo o GlassFish 3. Esta primeira parte apresenta uma visão geral e apresenta em mais detalhes fornecedores e qualificadores no CDI.

Fique por dentro
O CDI provê um mecanismo de injeção de dependências entre classes de uma aplicação Java EE 6, permitindo também o acesso por páginas Web a partir da linguagem de expressões unificada (EL). Dependências são associadas a contextos e tem seu ciclo de vida gerenciado automaticamente pelo container. Ao redor deste mecanismo, o CDI oferece uma série de funcionalidades como qualificadores, alternativas, decoradores, interceptadores e eventos que permitem uma grande flexibilidade no desenvolvimento da aplicação.

Sistemas de informação desenvolvidos na plataforma Java EE 6, utilizando ou não EJBs, podem se beneficiar bastante da utilização do CDI, pois o mesmo proporciona produtividade ao diminuir consideravelmente a quantidade de código de infraestrutura que deveria ser escrito para gerenciamento de dependências entre componentes, além de promover o desacoplamento entre classes, o que facilita a manutenção.

CDI – Contextos e Dependências

O CDI é o mecanismo oferecido pela plataforma Java EE 6 para gerenciamento de dependências entre componentes (chamados de beans) de uma aplicação corporativa (com ou sem o uso de EJBs), associando-os a contextos e oferecendo uma série de serviços de infraestrutura à aplicação. Em um artigo dividido em duas partes, apresentamos a maioria das funcionalidades oferecidas pelo CDI. Nesta primeira parte, apresentamos uma visão geral do seu funcionamento e aprofundamos nos conceitos de fornecedores e qualificadores. Começamos com um exemplo simples de injeção de dependências entre diferentes componentes de uma aplicação (Listagens 1 a 5 e Figuras 1 e 2). Em seguida, explicamos o que é um fornecedor e como o mesmo pode ser uma alternativa para definição de um bean (Listagens 6 a 9 e Figura 3). Finalmente, mostramos como beans e dependências podem ser qualificados (Listagens 10 a 12) para criação de categorias de dependências de uma mesma classe.

O padrão Java EE 6 foi aprovado e publicado pelo Java Community Process (JCP, veja Links) em dezembro de 2009 e com ele foi publicada também a JSR 299, chamada “Contexts and Dependency Injection for the JavaTM EE platform” ou, traduzindo em português, “Contextos e Injeção de Dependências para a plataforma JavaTM EE”. Este padrão, que também pode ser chamado de “Web Beans”, é mais conhecido pela sigla CDI, seguindo a tradição das tecnologias relacionadas a Java, que são geralmente citadas utilizando seus acrônimos.

Em um artigo dividido em três partes, publicadas nas edições 80, 81 e 82 da Java Magazine, apresentamos de forma prática várias das novidades do Java EE 6, inclusive o CDI. Agora, nosso objetivo é apresentar de forma mais aprofundada as funcionalidades do CDI, que compreendem, segundo a própria especificação:

Dada a extensão das funcionalidades do CDI, se fez necessário dividir este artigo em duas partes. Nesta primeira parte, apresentaremos uma visão geral da utilização do CDI para conectar beans que, interligados, implementam uma funcionalidade de um sistema. Em seguida, apresentaremos em mais detalhes duas das funcionalidades do padrão: fornecedores e qualificadores. Na segunda parte do artigo, a ser publicada na próxima edição da Java Magazine, falaremos mais sobre escopos e contextos, com foco no contexto da conversação, e apresentaremos as funcionalidades: alternativas, interceptadores, decoradores e eventos.

Ao longo do artigo, leitores familiarizados com o framework JBoss Seam irão notar bastante semelhança com o CDI. Tal semelhança não é mera coincidência, dado que o líder da especificação da JSR 299 foi Gavin King, criador do Seam. Gavin King é conhecido também por ser o criador do framework de mapeamento objeto/relacional Hibernate.

Exemplos e IDE utilizada

Os exemplos trazidos neste artigo fazem parte de um projeto do tipo Java EE / Enterprise Application criado com a IDE NetBeans 6.9.1 e foram testados no servidor GlassFish 3.0.1. Os exemplos fazem parte de uma aplicação Java EE fictícia com interface Web: uma agência que vende pacotes turísticos chamada “CDI Viagens”.

Nosso foco, no entanto, é o CDI e mostraremos nas listagens somente código-fonte relacionado ao mesmo. Para ver todos os fontes basta fazer o download do projeto associado a este artigo, disponível no site da Java Magazine. Além disso, para um melhor entendimento do artigo, é recomendada alguma familiaridade com Java EE 6, EJBs 3.0 e JSF 2.0. Para aprender mais sobre estas tecnologias e o uso do NetBeans para a construção da aplicação que utilizamos como exemplo, veja o artigo sobre novidades do Java EE 6 que, como citamos anteriormente, foi publicado nas edições 80, 81 e 82 da revista, além de diversos outros artigos sobre estas tecnologias publicados em edições anteriores da revista.

Por fim, é importante destacar que os exemplos mostrados neste artigo foram projetados para fins didáticos, ou seja, para o aprendizado sobre o CDI. Em muitos casos, utilizamos uma funcionalidade do CDI em um exemplo para poder demonstrá-la, mesmo que ela não seja a forma mais aconselhável para implementar a funcionalidade em questão. Cabe ao leitor, após aprender esta nova ferramenta, utilizá-la quando cabível.

Beans, contextos e dependências

Começaremos pela base do CDI que é a definição dos componentes em contextos, chamados de beans, e sua interligação pelo mecanismo de injeção de dependências. Após a criação do projeto e configuração de um leiaute obtido no site Free CSS Templates (veja Links) usando Facelets, implementamos a funcionalidade de cadastro de pacotes turísticos, como mostra a Figura 1. A figura mostra os pacotes turísticos já cadastrados (com nome e período no qual o pacote pode ser oferecido aos clientes) e, logo abaixo, um formulário para inserção de novos pacotes.

Nota: Beané traduzido literalmente para feijão, que não nos parece um nome adequado para estas classes. Poderíamos também traduzi-lo como “componente”, porém esta palavra é muito utilizada em diversos outros contextos. Para evitar confusão utilizaremos o termo em inglês, bean.
Figura 1. Tela de cadastro de pacotes turísticos do sistema CDI Viagens

Exceto para sistemas realmente muito simples, quando desenvolvemos sistemas de informação é interessante separar diferentes atribuições do sistema – como representação do domínio do problema, lógica de negócio e lógica de persistência – em módulos (em nosso caso, classes) diferentes. Porém isso nos cria um problema a ser resolvido: como estas classes irão comunicar-se entre si?

Na plataforma Java EE 6, o CDI provê uma solução a esta questão, permitindo-nos declarar tais classes como beans e indicar suas dependências, ou seja, de quais outras classes cada uma delas depende para realizar o seu objetivo. A Figura 2 mostra os principais componentes que implementam o cadastro de pacotes turísticos, divididos nas seguintes camadas:

Figura 2. Componentes do cadastro de pacotes turísticos

Começando pela camada de persistência, as Listagens 1 e 2 mostram, respectivamente, a interface PacoteTuristicoDAO e a classe PacoteTuristicoDAOJPA2, que a implementa. Como podemos ver na Listagem 1, o DAO oferece métodos clássicos de persistência: recuperação, gravação e exclusão. A implementação é feita utilizando JPA 2.0, porém a Listagem 2 não mostra a implementação dos métodos. Como já dissemos, nosso foco neste artigo serão as funcionalidades do CDI.

package br.com.javamagazine.cdiviagens.persistencia; import br.com.javamagazine.cdiviagens.dominio.PacoteTuristico; import java.util.List; import javax.ejb.Local; @Local public interface PacoteTuristicoDAO { long recuperarContagem(); List<PacoteTuristico> recuperarTodos(); List<PacoteTuristico> recuperarAlguns(int[] intervalo); PacoteTuristico recuperarPorId(Long id); PacoteTuristico salvar(PacoteTuristico objeto); void excluir(PacoteTuristico objeto); }
Listagem 1. Interface (local) do DAO para instâncias de PacoteTuristico
package br.com.javamagazine.cdiviagens.persistencia; import javax.ejb.Stateless; import javax.persistence.*; @Stateless public class PacoteTuristicoDAOJPA2 implements PacoteTuristicoDAO { @PersistenceContext private EntityManager em; /* Implementação dos métodos declarados na interface... */ }
Listagem 2. Stateless EJB que implementa o DAO de PacoteTuristico

É interessante notar, porém, as anotações que definem estes dois artefatos como um Stateless EJB: @Local indica que PacoteTuristicoDAO é uma interface local para um EJB, enquanto @Stateless define PacoteTuristicoDAOJPA2 como um EJB que não armazena estado. Além disso, no DAO já realizamos a primeira injeção de dependências. Uma instância da classe EntityManager (gerenciador de entidades), necessária para realização das operações de persistência via JPA, é injetada automaticamente pelo container na propriedade em, pois a mesma foi anotada com @PersistenceContext.

Recursos do Java EE, portanto, podem ser injetados por meio da utilização da anotação adequada. A plataforma provê as seguintes anotações para recursos Java EE:

Sendo assim, para ter o DAO de PacoteTuristico injetado automaticamente na classe CadastrarPacotes, que implementa a lógica de negócio, utilizamos a anotação @EJB, como mostra a Listagem 3. Ao declarar um atributo utilizando a interface do EJB e a anotação acima, o container cuida do resto e injeta automaticamente uma instância de PacoteTuristicoDAOJPA2 logo após ter criado a instância de CadastrarPacotes.

package br.com.javamagazine.cdiviagens.beans; import br.com.javamagazine.cdiviagens.dominio.PacoteTuristico; import br.com.javamagazine.cdiviagens.persistencia.PacoteTuristicoDAO; import java.text.*; import java.util.List; import java.util.logging.*; import javax.ejb.*; import javax.enterprise.inject.Model; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import javax.inject.Inject; @Stateful @LocalBean @Model public class CadastrarPacotes { private static final Logger logger = Logger.getLogger(CadastrarPacotes.class.getCanonicalName()); @EJB private PacoteTuristicoDAO pacoteTuristicoDAO; @Inject private Localizacao loc; private List<PacoteTuristico> pacotes; private PacoteTuristico pacote = new PacoteTuristico(); @Inject void carregarPacotes() { pacotes = pacoteTuristicoDAO.recuperarTodos(); logger.log(Level.INFO, "Carregando pacotes turísticos: pacotes carregados", pacotes.size()); } public List<PacoteTuristico> getPacotes() { return pacotes; } public PacoteTuristico getPacote() { return pacote; } public void cadastrar() { DateFormat df = loc.getFormatadorDatas(); NumberFormat cf = loc.getFormatadorDinheiro(); logger.log(Level.INFO, "Cadastrando pacote turístico: [nome = ; início = ; fim = ; preço = ]", new Object[] {pacote.getNome(), df.format(pacote.getInicioPeriodo()), df.format(pacote.getFimPeriodo()), cf.format(pacote.getPreco())}); pacoteTuristicoDAO.salvar(pacote); pacotes.add(pacote); FacesContext context = FacesContext.getCurrentInstance(); context.addMessage(null, new FacesMessage("Pacote turístico \"" + pacote.getNome() + "\" cadastrado com sucesso!")); pacote = new PacoteTuristico(); } }" [...] continue lendo...

Artigos relacionados