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.
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:
- Um ciclo de vida bem definido para objetos que armazenam estado (stateful), associados a contextos, sendo que o conjunto de contextos é extensível;
- Um mecanismo de injeção de dependências type-safe (i.e. verifica erros relacionados ao tipo de objeto em tempo de compilação), inclusive com possibilidade de escolher dependências diferentes em versões de desenvolvimento e produção, sem uma configuração excessivamente verbosa;
- Suporte à modularidade e ao modelo de componentes da plataforma Java EE;
- Integração com a linguagem de expressões unificada (Expression Language, ou EL), permitindo que um objeto contextual seja utilizado diretamente em uma página JSF ou JSP. Desta maneira, CDI integra os modelos de programação dos EJBs e do JSF;
- Capacidade de decorar objetos injetados e associar interceptadores a estes também de forma type-safe;
- Um modelo para notificação de eventos;
- Um contexto de conversação para a Web, além dos três contextos já definidos pela especificação Java Servlets (requisição, sessão e aplicação);
- Uma SPI (Service Provider Interface, ou Interface de Provedor de Serviços), que permite a integração de extensões portáveis ao container.
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.
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:
- Visão: a página JSF cadastrarPacotes.xhtml provê a interface com o usuário mostrada na Figura 1 – campos de formulário, botões, links e informações impressas na página;
- Negócio: a classe CadastrarPacote, implementada como um Stateful EJB sem interface, contém a implementação da lógica de negócio do caso de uso “Cadastrar Pacote Turístico”. A página JSF obtém desta classe as informações exibidas, além de enviar-lhe os campos do formulário para o cadastro de novos pacotes;
- Persistência: a gravação dos dados em um banco de dados é feita utilizando o padrão de projeto DAO – Data Access Object (Objeto de Acesso a Dados). A interface PacoteTuristicoDAO e um Stateless EJB que utiliza JPA 2.0 encontram-se nesta camada e são utilizados pela camada de negócio para o cadastro dos pacotes turísticos;
- Domínio: a classe PacoteTuristico representa o elemento do domínio do problema com o qual estamos lidando, isto é, o pacote turístico que será cadastrado. A classe é utilizada pelos componentes das outras três camadas.
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);
}
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... */
}
É 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:
- @PersistenceContext: para o gerenciador de entidades (ou contexto de persistência);
- @PersistenceUnit: para a unidade de persistência;
- @EJB: para EJBs, sejam eles remotos, locais ou sem interface;
- @WebServiceRef: para Web Services;
- @Resources: para outros recursos da plataforma 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
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Artigo