segunda-feira, 24 de setembro de 2012

Primeiros passos com CDI

Olá Pessoal!

Nesse post vamos falar sobre CDI 1.0, que está dentro da do JavaEE 6.

CDI é a especificação Java para injeção de dependência. Como toda nova especificação, CDI é orientada a anotações e tenta não ser intrusiva nos seus Beans. Se você quiser aprender o básico ou ver outros tutoriais/artigos como esse, sugiro que acesse o site do JDF (JBoss Developser Framework) e esse overview.

Para os mais avançadinhos, sim, CDI remove a necessidade de usar o DI SPRING!

Lembrando que embora tenha alguma teoria, a série de posts do "aprendendo-*" foca muito em parte prática, logo, configure o Eclipse para receber o JBoss AS 7!

Sobre Injeção de dependência e Inversão de controle

Você sabe que quando programa em Java você tem que instanciar as coisas. Por mais inocente que essa ação pareça, isso pode se tornar um problema com o tempo, pois quem instância uma classe fica ligado a esse "elo de instanciação", ou seja, se acoplam. Claro que alguém deveria inventar algo para melhorar isso e o fizeram! A injeção de depêndencia ou, como alguns chamam, Inversão de Controle foi a solução sugerida.
A forma proposta de "desacoplar" foi através da idéia de que basicamente você não vai mais instanciar as coisas, mas sim um "container"(daí a idéia de inversão de controle). Por exemplo, se sua classe X depende da classe Y e deve ter isso instanciada, você não vai usar o new, mas sim informar o container, ele vai injetar essa dependência(daí o nome Injeção de Dependência). 
Obviamente mágicas não existem, então você tem que informar o container de alguma forma que está fazendo isso. Alguns usam XML, outros anotações e outros usam simplesmente código Java.

Rod Johnson foi o primeiro a trazer isso para a plataforma Java com o famoso framework Spring que, inicialmente, usava XML e posteriormente passou a usar anotações. No entanto, recentemente isso foi incluido no Java nativamente através da especificação do CDI, onde até alguns pontos foram melhorados significando que você pode jogar fora o Spring e adotar JavaEE. CDI também traz o conceito de injeção dependendo do contexto que seu código foi chamado, mas hoje vamos ficar só com o DI do CDI :).

Blah, blah, blah, chega de histórinha da turma da mônica e vamos para o código *-*.

Um projeto WEB Simples

Vamos criar um projeto simples e inútil  para mostrar o funcionamento básico do CDI. 
A idéia é simples, vamos usar uma página JSP e um servlet. Esse servlet vai usar uma classe de serviço que só sabe cumprimentar as pessoas. 
Nosso primeiro passo é criar um projeto no Eclipse, logo em seguida criar um arquivo XML chamado beans.xml, que não faz nada, mas ativa CDI na sua aplicação. Com ele, o container(no nosso caso o JBoss) sabe que deve injetar coisas, sem ele, suas anotações de CDI são ignoradas. Então, criamos um arquivo beans.xml no diretório WEB-INF com o seguinte conteúdo:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemalocation="
      http://java.sun.com/xml/ns/javaee 
      http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>

Legal, agora é hora de criar classe que faz o cumprimento, olha abaixo a nossa classe:
public class DizedorOla{

 public String dizerOla(String nome) {
  if (nome == null || nome.isEmpty()) {
   return "Por gentileza, informe um nome";
  } else {
   return "Olá " + nome + "!";
  }
 }
}

Ótimo, agora podemos usar ela dentro de um servlet:

import java.io.IOException;

import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.wsiqueir.diversao.cdi.model.DizedorOla;

@WebServlet("/servletCDI")
public class SimplesServlet extends HttpServlet {
 private static final long serialVersionUID = 1L;

 @Inject
 private DizedorOla dizedorOla;

 protected void doPost(HttpServletRequest request,
   HttpServletResponse response) throws ServletException, IOException {
  String nome = request.getParameter("nome");
  request.getSession().setAttribute("mensagem", dizedorOla.dizerOla(nome));
  response.sendRedirect("index.jsp");
  
 }
}
Olhem! Uma anotação nova ali. A anotação @Inject diz que aquilo deve ser injetado e que eu não vou instanciar. Sem ela, o nosso servlet apresentaria "NullPointerException". Também percebam que eu direcionando o servlet para um JSP, segue o código do mesmo:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Testando CDI</title>
</head>
<body>
 <%
  String mensagem = (String) request.getSession().getAttribute(
    "mensagem");
  if (mensagem != null) {
 %>
 <h2>
  <%=mensagem%>
 </h2>
 <%
  }
 %>
 <form action="./servletCDI" method="post">
  <label>Seu nome:</label> <input name="nome" type="text" /> <input
   type="submit" value="Enviar para o servidor!">
 </form>
</body>
</html>

Se você executar esse código e acessar a página JSP, vai ver que isso vai funcionar sem NullPointerException pela mágica do JSP. Ó!!!!! Mas isso não é tão empolgante, fica mais legal agora. Digamos que você quer possibilitar a injeção de uma classe concreta em um servlet. Isso o meu amigo new pode fazer sem muitos problemas. Mas CDI é bem mais do que isso. Você também pode injetar classes concretas onde a variável é do tipo de uma interface, ajudando na implementação do padrão de projeto estratégia.
Para isso, colocamos o método dizerOla em uma interface:

public interface DizedorOla {
 public String dizerOla(String nome);
}


E criamos uma classe que implementa essa interface:


import javax.enterprise.inject.Default;

@Default
public class DizedorOlaFormal implements DizedorOla {

 @Override
 public String dizerOla(String nome) {
  if (nome == null || nome.isEmpty()) {
   return "Por gentileza, informe um nome";
  } else {
   return "Olá " + nome + "!";

  }
 }
}
Observe que estamos usando uma anotação @Default na declaração da classe. Ela simplesmente diz que se usarmos o @Inject e não "falarmos" mais nada para o container, ele vai injetar uma instância dessa classe! No servlet não mudamos nada, não vale a pena colocar o código aqui de novo :). Bem, esse é o padrão estratégia, logo podemos adicionar outras implementações para a interface DizedoraOla e permitir um novo comportamento na geração dessa mensagem lá no servlet. E se eu te disse que eu consigo fazer isso só adicionando uma anotação no servlet?  Seja a seguinte classe para dizer olá de uma forma diferente:)
import org.wsiqueir.diversao.cdi.qualifiers.Informal;

@Informal
public class DizedorOlaInformal implements DizedorOla {

 @Override
 public String dizerOla(String nome) {
  if (nome == null || nome.isEmpty()) {
   return "Fala um nome, cara.";
  } else {
   return "E aí " + nome + ", sussa?";
  }
 }
}
Veja que criamos uma anotação e anotamos a nossa classe com ela. Veja o código dela:
import org.wsiqueir.diversao.cdi.qualifiers.Informal;

@Informal
public class DizedorOlaInformal implements DizedorOla {

 @Override
 public String dizerOla(String nome) {
  if (nome == null || nome.isEmpty()) {
   return "Fala um nome, cara.";
  } else {
   return "E aí " + nome + ", sussa?";
  }
 }
}
Agora, se quisermos que o novo comportamente seja tomado no nosso Servlet, simplesmente temos que usar a anotação @Informal sobre o campo injetado! Simples, fácil e muito útil.
//...
 @Inject @Informal
 private DizedorOla dizedorOla;
//...
Isso é o que chamamos de qualificador, pois ele qualifica e identifica uma classe concreta pra ser injetada em um atributo de um tipo de sua interface

Conclusão


Mostramos a injeção de dependência usando CDI. Claro que não é só isso, temos muito mais para aprender, mas vocês podem ir além lendo a especificação ou varrendo mais tutoriais pela internet.

4 comentários:

  1. Muito bom William!
    Um dúvida, há algum caso que precisa ser configurado no XML ou é sempre essa mamata com anotações?

    ResponderExcluir
  2. Acho que sim. O beans.xml fica lá para quem quiser usar, mas tudo que vi até agora só envolvia anotações!

    ResponderExcluir
  3. Primeiramente Parabens pela iniciativa, muito legal você estar defendendo o JEE com tanta garra!

    Realmente o JEE parece que ficou melhor para se trabalhar, incorporando as várias funções de frameworks que foram surgindo ao longo do tempo. Mas, como a maioria dos desenvolvedores trabalham para um cliente ou empresa que não visa melhorias de desenvolvimento e sim produtos novos cada vez mais rapido, é impossível modificar o que já foi feito anteriormente. Então acaba que todos preferem trabalhar com o Spring, que já tem o ambiente todo montando e todo o código suportado por ele, do que mudar a tecnologia e se adequar ao JEE, que na visão das empresas é perda de tempo. Infelizmente =/...

    Salva a exceção que sem sombra de dúvidas, para empresas que estão começando ou projetos que estão se iniciando agora, vale a pena começar com JEE, eliminando os vários frameworks que são utilizados hoje com o objetivo de aumentar a produtividade e simplesmente ficar com o combo Maven + JEE (:

    Abraços,

    Eclis Rodrigues de Castilho
    Desenvolvedor Java.

    ResponderExcluir
  4. primeiramente parabens e foi muito util...

    ResponderExcluir