sexta-feira, 27 de março de 2015

CDI Eis a Questão


Pra quem não sabe, CDI (Contexts and Dependency Injection) é a especificação do JEE 6 para injeção de dependências e é uma ótima ferramenta de se trabalhar. Então lá vai!
Uma das coisas mais interessantes de CDI é que ela é uma tecnologia capaz de integrar muito bem com outras tecnologias, como por exemplo JSF, JPA, EJBs, etc. Com a chegada do CDI 1.1 (JEE 7) ainda é possível se integrar com outras coisas que não entrarei no mérito por enquanto.



1
2
3
4
5
6
7
private EntityManager em;
private EntityManagerFactory emFactory;
private void createEntityManager() throws Exception{
     emFactory = Persistence.createEntityManagerFactory("MyPU");
     em = emFactory.createEntityManager();
}



Analisando bem, esse DAO se preocupa com MUITA coisa! Basicamente, estamos criando uma conexão com o banco de dados dentro do DAO. Isso não faz muito sentido, certo?


1
2
3
4
5
private EntityManager em;
public DAO(EntityManager em){
    this.em = em;
}



Bem, a resposta é curta e grossa: Sim! Alguém uma hora terá que cuidar de todas as dependências, mas, você pode criar uma classe que faça exatamente isso, que cuide de todas as dependências de todas as classes.
Resumidamente, falamos que um container/framework possui IoC quando ele tem a capacidade de trabalhar com a aplicação que utiliza o container/framework em questão, não precisando o programador utilizá-lo explicitamente. Containers e frameworks IoC são mais comuns do que parece. Qualquer framework que faz alguma coisa com uma classe que você criou sem você delegar programaticamente, implementa isso que chamamos de inversão de controle.


1
2
@PersistenceContext
private EntityManager em;
1
2
3
4
5
6
public class DAO {
     @PersistenceContext
     private EntityManager em;
     //crud methods
}
1
2
3
4
5
6
7
8
9
10
11
12
public class Service {
     @Inject
     private DAO dao;
     public void save(Entity e){
         e.validate();
         dao.save(e);
     }
     //methods
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Service {
     private DAO dao;
     @Inject
     public Service(DAO dao){
         this.dao = dao;
     }
     public void save(Entity e){
         e.validate();
         dao.save(e);
     }
     //methods
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Service {
     private DAO dao;
     public void save(Entity e){
         e.validate();
         dao.save(e);
     }
     @Inject
     public void setDao(DAO dao){
         this.dao = dao;
     }
     //methods
}



Vamos usar como exemplo uma classe bastante usada:


1
private Logger logger = LoggerFactory.getLogger(MyClass.class);
1
2
3
4
5
6
private Logger logger;
@Produces
public Logger loggerProducer(){
    return LoggerFactory.getLogger(MyClass.class);
}
1
2
@Inject
private Logger logger;
1
2
3
public interface DAO {
     //crud methods
}
1
2
3
public class PersonDAO implements DAO {
     //crud methods
}
1
2
3
public class ProductDAO implements DAO {
     //crud methods
}
1
2
@Inject
private DAO dao;
1
2
3
4
5
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface PersonDAOQualifier {
}
1
2
3
4
5
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface ProductDAOQualifier {
}
1
2
3
4
@PersonDAOQualifier
public class PersonDAO implements DAO {
     //crud methods
}
1
2
3
4
@ProductDAOQualifier
public class ProductDAO implements DAO {
     //crud methods
}
1
2
3
@Inject
@PersonDAOQualifier
private DAO dao;



Por exemplo, vamos criar um interceptor que loga o que vem e o que saí de um método:


1
2
3
4
5
6
7
8
9
10
11
public class LoggerInterceptor{
    @Inject
    private Logger logger;
    @AroundInvoke
    public Object loggerInterceptor(InvocationContext ctx) throws Exception{
         logger.info("Method: " + ctx.getMethod());
         Object result = ctx.proceed();
         logger.info("Returned: " + result);
    }
}

O método alvo é executado quando você chama o método ctx.proceed(); e o retorno do método alvo é retornado nesse mesmo método proceed.

1
2
3
4
5
6
7
@Interceptors({LoggerInterceptor.class})
public class Service {
     @Inject
     private DAO dao;
     //methods
}
1
2
3
4
5
6
7
8
@Named
@RequestScoped
public class ManagedBean implements Serializable{
     @Inject
     private Service service;
     //methods
}
1
<h:commandButton action="#{managedBean.someAction}" value="Some Action"/>



De qualquer forma, utilizando as anotações do CDI você vai conseguir obter as mesmas funcionalidades. O principal problema é a confusão que podemos fazer misturando anotações das duas apis. Uma confusão bastante comum é trocar as anotações de scopos, sendo que de ambas as apis possuem o mesmo nome isso realmente acaba confundindo, mas, esclarecendo, utilize apenas as anotações do pacote javax.enterprise.context para os escopos.
Não falei mas, todo bean do CDI tem um escopo. O padrão é o @Dependent que faz com que nosso bean seja instanciado a mesma quantidade de vezes de quem estiver injetando for.
Outros escopos disponíveis são: @RequestScoped, @SessionScoped, @ApplicationScoped que não vou explicar aqui e agora como funcionam.
CDI funcionam também muito bem em servlet containers, como por exemplo com o Tomcat ou o Jetty, existem vários exemplos na internet explicando como integrá-los.

Nesse artigo eu falarei muito pouco mesmo em relação ao CDI, apenas citarei coisas úteis que talvez possa fazer falta no seu dia a dia de trabalho e que o CDI propõe.
Mas o que é essa tal de injeção de dependências?
Então, é um assunto que me fez pensar muito quando comecei a usar, me fiz várias perguntas, coisas tipo: Por que usar? O que eu ganho com isso? Qual a vantagem que um sistema que usa injeção de dependências contra um sistema que não usa?

Pois bem, vou explicar de uma maneira que faria com que eu fosse esclarecido na época, espero que esclareça a dúvida de quem se pergunta a mesma coisa. Você sabe o que é um design pattern, certo? Injeção de dependências é um deles, no qual diz que a sua classe não deve se preocupar em como obter as suas dependências e sim em fazer apenas o que a ela diz respeito. Isso torna seu código mais limpo e coeso, por exemplo:

Digamos que vamos criar um DAO, já que estamos usando JPA, precisamos ter o nosso EntityManager. Nosso DAO, além de se preocupar em manipular dados com a database, ele também precisa se preocupar em como obter o EntityManager, que também faz com que ele precise utilizar o EntityManagerFactory para criar criá-lo, sendo assim, ele também precisaria utilizar a classe Persistence para criar o EntityManagerFactory, não esquecendo também que ele também precisaria conhecer o nome do Persistent Unit.
Obs.: Eu reparei que as pessoas costumam confundir muito o conceito de inversão de controle e injeção de dependencias. Muitas acham que ambos tem o mesmo significado e esse é um pensamento errado. Como eu sei que vocẽ pode não ter entendido a minha explicação, aconselho dar uma olhada nesse artigo do Martin Fowler sobre IoC e DI

Então, com a inversão de controle, os containers tiram o controle da sua mão e passam o controle para eles mesmos. Sim, quanta criatividade nesse nome. Aliás, um pouco controverso, pois, na maioria das vezes, são bastante configuráveis pelo programador. Você consegue delegar a eles o que eles precisam fazer, e eles futuramente vão fazer, sem você precisar intervir. Continuando com esse pensamento, você logo imagina que IoC + DI = injetador de dependências automatizado, ou seja, o próprio container injetaria as instâncias para você. Felizmente, você está completamente certo, parabéns!
Ao invés daquela bananada de código toda apenas para obter um EntityManager, só com uma anotação todo aquele trabalho é economizado, além de diminuir o acoplamento da nossa classe e aumentar a sua coesão!

Começando agora com o CDI, criaremos uma classe que utilizaria o DAO. Por exemplo, vamos criar uma classe Service. Sabendo que toda a classe é elegível para injeção. Uma anotação nova: @Inject, é a anotação mais famosa do CDI, uma das que você irá mais usar, ela é a chave da injeção de dependência dessa ferramenta tão interessante. Você pode utilizar a anotação não só no atributo, mas também em um método ou no construtor. Além de você poder fazer com que uma classe sua seja um bean elegível para injeção, você também pode fazer com que outras classes que você não criou também sejam elegíveis para injeção.Não vê outra maneira de conseguir uma instância dessa classe? Veja bem. Sempre que você for usar a classe Logger:

O que é retornado no método loggerProducer() é passado para qualquer atributo Logger que possua a anotação @Inject. Aprendemos mais uma maneira de criar um bean elegível para injeção, mas, e se eu tivesse dois beans elegíveis para o mesmo tipo? Por exemplo:

Imaginem que tenhamos uma interface chamada DAO e duas classes que a implementam, a PersonDAO e a classe ProductDAO:

Quando você for fazer isso o container vai lhe perguntar qual das duas instâncias da classe DAO é para ele instanciar: PersonDAO ou ProductDAO. Uma saída é criar um qualifier. Como o próprio nome já diz, um qualificador, é nada mais que uma anotação, basicamente funciona como uma identidade para um bean. Outra coisa bacana no CDI é o uso de Interceptors. Como o próprio nome já diz, um interceptador é executado antes e depois de um determinado método para fazer alguma ação que você determinar.

Um ponto negativo é que as anotações do CDI não são compatíveis com as do JSF, por exemplo: você não vai conseguir utilizar o @Inject caso esteja em uma classe que utiliza a anotação @ManagedBean, simplesmente não irá funcionar.

Lembrando que CDI é apenas uma especificação, ou seja, você irá precisar da implementação. A JBoss possui uma implementação da CDI com alguns poucos recursos específicos a mais, chamada JBoss Weld, sendo a implementação hoje da CDI mais famosa. Usando servidores JEE 6/7 você não precisará se preocupar com implementações, é só você colocar o jar da JEE e simplesmente tudo irá funcionar.

Para seu projeto CDI funcionar, é necessário adicionar um arquivo beans.xml, mesmo que vazio dentro da pasta WEB-INF do seu projeto, caso seu projeto seja Web ou dentro da pasta /src/META-INF caso seu projeto não seja Web, como por exemplo um projeto EJB.

Então é isso ai cambada! espero que tenha sido útil. Se foi blz e se não foi blz também :p FUI!

Admin: Bruno

Olá Galera! muito grato por estarem acessando nosso blog. Espero que seja possível transmitir de forma compreensível um pouco de meus conhecimentos em programação, para esta comunidade de desenvolvedores que cresce cada vez mais! Espero que Gostem! Abraço! E meu enorme obrigado à Renato Simões, Átila Soares,Wanderson Quinto, Emerson e a toda galera que sempre ajudou meu sincero obrigado....
Especialmente a Natalia Failache e Rita de Cassia que sempre apoiaram este sonho....

De seu amigo Bruno Rafael.