Use CDI no seu próximo projeto Java

CDI é a especificação do Java EE 6 que cuida da parte de injeção de dependências. E, além de ser oficial e estar incluída em todos os servidores de aplicação, é tão boa e produtiva que já tem gente questionando o papel do Spring nos dias de hoje.

O CDI se encaixa muito bem em todo tipo de projeto Java. Se você usa JSF2, usar CDI é natural, como mostramos no nosso curso FJ-26. Mas mesmo para aplicações Web simples, com apenas Servlets, o CDI é um grande ganho.

Habilitando CDI

Habilitar o CDI no projeto é muito simples. Se você já está usando um servidor Java EE 6, basta criar um arquivo vazio chamado beans.xml na pasta META-INF do seu projeto (ou WEB-INF num projeto web). Esse é um simples arquivo de marcação e apenas sua presença já faz com que o servidor habilite o suporte a CDI e escaneie suas classes automaticamente.

Se você estiver usando 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. Vamos usar o Weld, a implementação de referência e a mais usada (já embutida no JBoss e no Glassfish).

No caso do Tomcat, os passos de configuração são:

Feito isso, basta criar o tal arquivo META-INF/beans.xml vazio no projeto.

Tudo são beans

Com o CDI habilitado, 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.

Aliás, essa anotação @Inject é a base de todo o CDI: é ela quem permite a injeção de dependências e devemos usá-la nos pontos que queremos injeção automática, sejam construtores, setters ou atributos privados.

Essa anotação, embora usada com CDI, faz parte na verdade de uma outra especificação, a JSR330. Essa é uma especificação bastante simples, com apenas algumas anotações relacionadas à injeção de dependências no pacote javax.inject. A ideia é ter um conjunto básico de recursos para injeção a serem suportados em todos os frameworks do tipo – além do CDI, Spring, Google Guice, PicoContainer e outros frameworks suportam essas anotações.

Primeira injeção

Imagine que temos uma classe ProdutoDAO que cuida de persistir objetos Produto:

public class ProdutoDAO {
   public void adiciona(Produto produto) {
      // ... implementação
   }
}

Essa classe simples, sem nenhuma configuração particular, pode ter objetos injetados em qualquer outro ponto da aplicação. Basta usar a anotação @Inject:

public class ProdutoController {
    @Inject private ProdutoDAO dao;

    public String inserir() {
        Produto p = // ...
        dao.adiciona(p);
        return "Produto adicionado!";
    }
}

Poderíamos também ter feito a própria classe ProdutoDAO receber um EntityManager da JPA para trabalhar com a persistência:

public class ProdutoDAO {
   @Inject private EntityManager manager;

   public void adiciona(Produto produto) {
      manager.persist(produto);
   }
}

Produção de objetos

Sempre que encontrar um ponto de injeção, o CDI vai tentar instanciar a classe sendo referenciada. No caso do DAO anterior, ao criá-lo, o CDI vai tentar dar new em EntityManager.

Mas EntityManager, assim como vários outros objetos, não pode ser criado tão facilmente assim. A criação de um EntityManager exige a chamada do método createEntityManager no EntityManagerFactory correspondente. É um objeto com um processo de fabricação particular.

Nos Design Patterns, sempre que encontramos esse tipo de cenário, criamos uma Factory para aquele objeto. É um simples método que encapsula a criação não-trivial do objeto em questão. Com CDI é a mesma coisa.

Vamos criar um método em uma classe qualquer do sistema que cuide da produção de objetos EntityManager. E podemos indicar ao CDI que queremos que ele use esse método sempre que alguém pedir a injeção de um EntityManager. Fazemos isso com a anotação @Produces:

public class ProdutorEntityManager {
   private static EntityManagerFactory factory = Persistence.createEntiyManagerFactory("default");

   @Produces
   public EntityManager criaEntityManager() {
      return factory.createEntityManager();
   }
}

Agora, todos os pontos de injeção que precisarem de EntityManager irão invocar essa fábrica anotada com @Produces.

Escopos

Mas quantos EntityManagers vamos criar com o código acima? Por padrão, toda dependência no CDI possui um escopo chamado Dependant. Isso é: será instanciada tantas vezes quanto quem estiver chamando for. Certamente não é o que queremos com nosso EntityManager.

É usual criar um EntityManager por requisição em uma aplicação Web. Podemos indicar isso apenas anotando o método produtor com @RequestScoped:

public class ProdutorEntityManager {

   @Produces @RequestScoped
   public EntityManager criaEntityManager() {
      // ...
   }
}

Agora, um novo EntityManager será criado associado a um request. Se mais de uma classe pedir um EntityManager durante o mesmo request, a mesma instância será passada, evitando desperdício de recursos. Outros escopos comuns são @SessionScoped e @ApplicationScoped.

Mais: como definimos o escopo da dependência, temos agora uma visão clara de seu ciclo de vida. O objeto durará apenas um request, e será descartado quando o request acabar.

O CDI nos deixa, inclusive, executar algo na fase de descarte da dependência, ao final da requsição. No nossa caso, será muito útil para chamar o close no EntityManager. Basta criar um método que receba a dependência a ser descartada e anotar o parâmetro com @Disposes:

public class ProdutorEntityManager {

   @Produces @RequestScoped
   public EntityManager criaEntityManager() {
      // ...
   }

   public void finaliza(@Disposes EntityManager manager) {
      manager.close();
   }
}

O método finaliza será chamado automaticamente pelo CDI ao final do request e fará o fechamento do EntityManager.

Use CDI no seu próximo projeto

O CDI é extremamente completo e poderoso. Esse artigo mostrou apenas o básico e o início do trabalho. Mas seu container de injeção typesafe com anotações simples e produtivas tem muitos outros recursos. Mostrei várias funcionalidades do CDI em uma palestra no JavaOne Brasil que você pode acompanhar a seguir:

Aqui mesmo no blog já mostramos como fazer a produção de dependências de tipos genéricos. Além disso, há recursos como qualifiers, interceptadores, decorators, eventos e muito mais. Mostramos várias dessas funcionalidades no curso FJ-26 da Caelum, inclusive com o JBoss Seam 3, uma ótima companhia para o CDI.

E você, o que acha do CDI? Tem usado em seus projetos?

75 Comentários

  1. Daniel 07/05/2013 at 18:25 #

    Uma vez usando CDI da até preguiça de pensar no Spring..
    só tem um problema: qual framework MVC (action based) trabalha bem com CDI hoje em dia?

  2. Alberto Souza 07/05/2013 at 18:33 #

    Oi Daniel,

    Estamos trabalhando na integração do VRaptor com ele :).

    Abraço!

    Alberto

  3. Bruno 17/05/2013 at 10:42 #

    Sergio, no VRaptor, como ele faz para ver as dependencias no construtor e injeta-las? Qual classe é responsável por isso?

  4. Diego Souza 04/06/2013 at 21:17 #

    Sérgio, posso usar CDI em projetos desktop sem problemas?

  5. Alexsandro 21/06/2013 at 10:58 #

    quantos arquivos JAR´s tem a implementação Weld? no site do desenvolvedor o arquivo zipado é uns 40MB, mas acredito que não é somente as bibliotecas mas documentação e outros arquivos. pergunto porque minha banda é estreita!

  6. Claudio 12/09/2013 at 21:43 #

    Sergio, otimo post!
    Se puder me esclarecer duas duvidas, por favor:
    – para as requisicoes coloco o metodo producer com escopo de request, em um processo agendado com timer service ou quartz, qual deve ser o escopo?
    – é necessário o metodo para fechar o entity manager, ja que ele será enceraddo no final da requisicao?

    Obrigado

  7. Luciano Pontes 01/10/2013 at 08:15 #

    Parabéns pelo ótimo post amigo!

  8. Danilo 17/10/2013 at 16:53 #

    Como utilizar ViewScope no CDI ?

  9. Luiz Augusto Mello 07/12/2013 at 19:17 #

    Estou com problemas ao configurar o WELD no Tomcat 7, alguma solução? Li que há um bug onde o WELD reconhece o Tomcat 7 como Tomcat 6. Att.

  10. Cristiano 12/12/2013 at 12:14 #

    Quando eu subo meu tomcat o hibernate não cria as tabelas, somente após algum request na aplicação, alguma sugestão?

  11. ALBERTINO 10/04/2014 at 17:15 #

    Excelente.

    Obrigado.

  12. Roberto Ferreira 29/05/2014 at 16:42 #

    Olá Sérgio. Sou iniciante CDI rs, e achei interessantíssimo seus artigos. Estou tendo um problema aqui e não entendi uma coisa muito bem. Tenho uma GenericDao, dentro dela faço um @Inject EntityManager manager; como sua implementaçao sugere. Mas o meu manager sempre fica nulo. Andei dando uma lida e eu preciso fazer uma factory para o meu GeneriDao, e passar o em através de um construtor public do genericDao. Eu preciso mesmo fazer isso? Uma factory para o meu GenericDAO? Se sim? Pq?

    Obrigado =).
    Excelente post!

  13. Atila 20/08/2014 at 18:46 #

    Boa noite Sergio,
    Ainda estou engatinhando nesse mundo porem me tira uma duvida por gentileza me tira uma duvida o CDI com @Poduces e @RequestScoped poderia substituir um pool de conexões como c3po?

    Ótimo post!!!

  14. Antônio 21/08/2014 at 09:13 #

    Artigo bem escrito e muito esclarecedor para quem está começando a estudar JSF, que é o meu caso.
    Excelente artigo! Obrigado.

  15. ILDO ZACARIAS RIBEIRO JÚNIOR 09/04/2015 at 07:58 #

    Parabéns pelo artigo, material de altíssima qualidade;

  16. André Costa 15/07/2015 at 00:23 #

    Show !! Parabéns pelo post!

  17. Thiago Moreno 17/11/2015 at 09:41 #

    Implementei um projeto com CDI já que as Anotações do javax.faces já estão marcadas como obsoletas, porem ao utilizar o Spring-Data JPA as anotações de CDI não estão funcionando corretamente, os managed beans anotados com @requestScope e ate mesmo os @session funcionam como se fossem @Application. Acredito que seja algum problema do scopo singleton dos beans do spring. Tive que tirar as anotações do CDI e usar as do pacote javax.faces

  18. Antonio Manoel Coutinho Lopes 12/04/2016 at 20:19 #

    Valeu!!!

    Parabéns pelo artigo, esclarecedor e vamos usar CDI de agora em diante. Vou reescrever um projeto bem pequeno pra testar.

  19. Diego Moreira 07/08/2016 at 00:36 #

    Boa noite Sérgio.
    Estou usando em um projeto Netbeans com Java EE 7, CDI, Hibernate e PrimeFaces.
    Gostei muito do seu post e estou em dúvida porque estou usando JTA, dessa forma não vou precisar criar um Factory para produzir o EntityManager. Nessa situação minha como devo proceder?

  20. Edison L. Werle 14/10/2016 at 17:32 #

    Estava usando @RequestScoped até mudar a versão do hibernate para 5.2.3 e funcionava blz. Após troca-lá não consegui mais pois dava erro de Cast ao tentar buscar a sessão do Hibernate atrás do EntityManager injetado. Mudei para @Dependent e voltou a funcionar. Acho que não está muito certo isto né.

Deixe uma resposta