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.
Saiba mais: como utilizar o CDI em seus projetos.
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; }
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(); } }
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); } }
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; } }
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.