Motivação

Em contextos em que se está empregando o padrão injeção de dependência em Java, na maioria das vezes o CDI sabe exatamente como criar um objeto a partir da anotação @Inject. No entanto, há casos em que é necessário “ensiná-lo” como criar determinado objeto que demanda certas peculiaridades. Neste artigo veremos como proceder nessas situações utilizando a anotação @Produces, capaz de auxiliar o CDI na construção de um bean.

Como funciona o @Produces

Em casos nos quais a criação de um bean requer a execução de procedimentos adicionais, além da chamada padrão ao seu construtor, para que o CDI consiga criá-lo de maneira correta, ele procura no classpath do projeto todos os métodos e propriedades que possuem a anotação @Produces e que retornem o tipo de objeto que ele precisa. Para compreender seu funcionamento, tomemos como exemplo um bean chamado Person, cujo código é apresentado a seguir:


    package br.com.devmedia;

public class Person {}

Podemos injetar esse bean em uma classe Service, que está sendo gerenciada pelo CDI. Para isso, utilizamos a anotação @Inject, como mostrado na Listagem 1.

package br.com.devmedia;
      import javax.enterprise.context.RequestScoped;
      import javax.inject.Inject;
      import javax.inject.Named;
      @Named
      @RequestScoped
      public class Service {
          @Inject
          private Person person;
      }
Listagem 1. Classe Service injetando a classe Person

Nesse caso, quando o objeto do tipo Person for injetado, o CDI saberá que deve chamar o construtor dessa classe. Porém, podem haver situações em que se torna necessário criar esse bean de uma forma diferente, por exemplo, a partir da chamada a um web service. Em situações como essa, precisamos criar um produtor (Producer) capaz de criar um objeto Person conforme desejamos, como mostra a Listagem 2.

package br.com.devmedia;
      import javax.enterprise.inject.Produces;
      public class PersonProducer {
      
          @Produces
          public Person createPerson(){
              return callWebService();
          }
      }
Listagem 2. Criando um Producer para a classe Person

Nesse exemplo, a classe PersonProducer abstrai a lógica de criação do objeto Person, que aqui simula a chamada a um web service. Perceba que o método createPerson(), independentemente da sua lógica, sempre deverá retornar um objeto do tipo Person e também possuir a anotação @Produces. Essas duas características são suficientes para o CDI entender qual método ele deve chamar a fim de criar um objeto do tipo Person.

Criando um Producer para um EntityManager

Agora que já compreendemos como o CDI trabalha para criar instâncias de objetos injetados dinamicamente, veremos um novo exemplo que ilustra um caso muito comum em projetos Java: a criação de um EntityManager proveniente do JPA através de um CDI Producer. O CDI, por si só, não sabe como criar um EntityManager, o que significa que usar apenas o @Inject não resolveria, pois ele sempre retornaria um objeto

Para simular essa situação, temos na Listagem 3 um exemplo de injeção do EntityManager em uma classe DAO, responsável por realizar as operações direto na base de dados.

package br.com.devmedia;
      import javax.enterprise.context.RequestScoped;
      import javax.inject.Inject;
      import javax.inject.Named;
      import javax.persistence.EntityManager;
      @Named
      @RequestScoped
      public class DAO {
          @Inject
          private EntityManager em;
          public void save(Object o){
              em.persist(o);
          }
      }
Listagem 3. Injetando o EntityManager no DAO

Ao chamar o método save(), teremos uma NullPointerException, pois o objeto em terá valor nulo, uma vez que o CDI não sabe como criar uma instância de EntityManager. Para solucionar esse problema, precisaremos criar um Producer, semelhante ao que fizemos na Listagem 2. Na Listagem 4 podemos ver o código da classe EntityManagerProducer, solução para o nosso problema.

 package br.com.devmedia;
      
       import javax.enterprise.inject.Disposes;
       import javax.enterprise.inject.Produces;
       import javax.persistence.EntityManager;
       import javax.persistence.EntityManagerFactory;
       import javax.persistence.Persistence;
       import java.io.Serializable;
      
       public class EntityManagerProducer implements Serializable {
      
          private static final long serialVersionUID = 1L;
          private static EntityManagerFactory emf =
       Persistence.createEntityManagerFactory("persistenceUnit");
      
          @Produces
          public EntityManager createEntityManager() {
              return emf.createEntityManager();
          }
      
          public void close(@Disposes EntityManager em) {
              if (em.isOpen()) {
                  em.close();
              }
          }
      
          public EntityManagerFactory getEmf() {
              return emf;
          }
      
          public void setEmf(EntityManagerFactory emf) {
              this.emf = emf;
          }
       }
Listagem 4. Criando um Producer para o EntityManager
Linha 13: Criamos um EntityManagerFactory apontando para nosso persistenceUnit, contido dentro do arquivo persistence.xml. Esse objeto servirá para criar o EntityManager;
Linha 16: A partir daqui começamos a produzir o EntityManager. Perceba que o método retorna um objeto desse tipo e possui a anotação @Produces. Portanto, ele será chamado pelo CDI quando necessário;
Linha 21: A anotação @Disposes faz o inverso da @Produces, ou seja, destrói a instância da classe injetada. Assim, quando o EntityManager for destruído, o método close() da classe EntityManagerProducer será chamado, fechando a conexão com o banco de dados através do método em.close().

A partir dessa implementação, quando o CDI precisar criar um EntityManager, ele irá produzi-lo e destruí-lo de acordo com a lógica implementada na classe EntityManagerProducer.

Saber como o CDI funciona é fundamental para que se obtenha os resultados esperados quando for necessário utilizar a injeção de dependências, garantindo que os objetos sejam criados corretamente pelo framework.