Desenvolvendo uma aplicação Desktop com Weld – Parte 1

Como havia comentado, em Novembro do ano passado eu apresentei uma palestra no Javaneiros, evento anual sobre Java organizado pelo JUGMS. Os slides eu já disponibilizei no post anterior, e agora vou de fato colocar aqui no blog o que mostrei na palestra, pois slides sem explicação não serve para muita coisa né?

Como acho que um post só vai ficar muito grande, vou publicar o conteúdo em partes.


Intradução


Primeiramente eu gostaria de explicar, para quem não está familiarizado, o que é a CDI. Hoje em dia está mais difundido que na época da palestra, mas até para manter a coerência com os slides vou comentar rapidamente alguma coisa. Se seu objetivo é ver código, pode pular esse tópico.

A CDI, ou “Context and Dependency Injection for the Java EE platform” é uma especificação que define uma forma padrão de trabalharmos com DI em aplicações Java. Pelo nome já vemos que ela é voltada para Java EE, mas está seguindo os mesmos passos da JPA, que foi introduzida no Java EE 5 mas é usada sem problemas em aplicações SE.

Talvez a coisa que valha mais a pena comentar antes de entrar na prática é o monte de nomes envolvidos nessa JSR. O primeiro “nome” é a própria identificação da JSR: “JSR-299″. Acho que até quem nunca foi ligado em número de JSR já ouviu falar dessa tal de 299. Para resumir a história, ela já se chamou WebBeans antes de se chamar CDI, e até hoje muita coisa na internet, principalmente as discussões iniciais você vai encontrar esse nome. Além disso tem a implementação de referência dela, que também já chamou WebBeans e agora é Weld. Ou seja, de WebBeans não sobrou nada.

Outra coisa que veio dar uma cofundida nesses nomes foi a JSR-330, “Dependency Injection for Java“, que nada mais é do que a extração das anotações da CDI para uma especificação menor, tornando assim mais fácil que frameworks como Spring e Guice utilizem anotações padrão mesmo não sendo uma implementação da CDI. Isso gerou muita polêmica também, pois em tese a CDI já deveria ser mínima e não faz sentido alguém usar as anotações dela sem ter o mesmo comportamento. Mas isso já é outra história.


Ainda antes de começar…


A CDI sofreu muita influência do Seam e Guice, além é claro de não ser difícil de compreender para quem trabalha com Spring, pois no fim das contas os conceitos são os mesmos.

Uma premissa muito forte da CDI é a injeção de dependência “type safe“, o que já a diferencia bastante do Seam que trabalha muito com Strings. Nesse ponto já fica mais perto para quem trabalha com Spring do que quem tá acostumado com Seam.

Outra coisa que temos que ter em mente é que diferentemente do Spring ou Seam onde uma classe simples não é considerada uma candidata à injeção de dependência a menos que seja marcada (via xml ou anotação), na CDI se a classe está dentro de um bean package então ela automaticamente é elegível. Mas isso nós vamos ver na aplicação de exemplo.

Outra coisa que temos que ter em mente é que o objetivo da CDI não é substituir o Seam. O Seam tem um mundo de coisas como facilitadores para e-mail, pdf, bpm, segurança, jsf, etc. A CDI define apenas o core o que será o core do Seam 3, ou seja, a parte da DI, interceptors e mais algumas coisas. Então não vamos comparar coisas tão diferentes com comentários tipo “O Seam é muito melhor que o Weld. O Weld não faz nada, o Seam faz um monte de coisas”.

A primeira coisa que precisamos saber para trabalhar com a CDI é o que é um bean package. Na prática um bean package vai ser um jar (ou pode estar explodido mesmo) onde temos um arquivo META-INF/beans.xml. Se a CDI encontrar esse cara, ela escaneia tudo e considera todas classes dentro desse “módulo” como beans injetáveis. É análogo ao seam.properties, mas no caso do Seam ainda precisa ter a anotação ou estar no components.xml para a classe ser considerada.

Outra coisa que vamos ver no exemplo é que o mesmo se trata de uma aplicação SE de console. Aí nos perguntamos: mas no nome da especificação não fala que é para Java EE? Pois é, mas a especificação define também a criação de extensões portáveis. Isso quer dizer que você pode fazer uma extensão da CDI e rodar em qualquer implementação dela. Pois bem, o Weld tem uma extensão que permite que rodemos tudo isso num ambiente SE, porém com limitações, claro.


Agora vamos à pratica


O nosso exemplo é bem simples, e consiste em realizarmos um saque e um depósito em uma conta bancária via um caixa eletrônico. Para simplificar o exemplo, temos apenas uma conta e um caixa eletrônico.

Nossa aplicação inicia com a classe MovimentacaoControl.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package br.com.jugms.weldse.control;
 
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
 
import org.jboss.weld.environment.se.events.ContainerInitialized;
 
import br.com.jugms.weldse.model.CaixaEletronico;
import br.com.jugms.weldse.model.ContaBancaria;
 
/**
* @author http://gilliard.eti.br
*/
@ApplicationScoped
public class MovimentacaoControl{
 
    @Inject private ContaBancaria contaBancaria;
    @Inject private CaixaEletronico caixaEletronico;
 
    public void executar(@Observes ContainerInitialized init) {
 
        System.out.println("hello");
 
        caixaEletronico.sacar(200.0f);
 
        System.out.println(contaBancaria.getSaldo());
 
        caixaEletronico.depositar(300.0f);
 
        System.out.println(contaBancaria.getSaldo());
 
    }
}

Como podemos ver essa classe possui a anotação @ApplicationScoped, mas essa anotação não serve para habilitar o gerenciamento dos objetos dessa classe, pois como eu disse anteriormente, isso é automático já que estamos em um bean package.

Essa classe também não possui um método main. Para executar o exemplo executamos a classe org.jboss.weld.environment.se.StartMain, e essa sim possui um método main, que inicializa todo o framework e quando termina de inicializar o container ele avisa lançando um evento do tipo org.jboss.weld.environment.se.events.ContainerInitialized. Para iniciarmos nossa aplicação, basta escutarmos esse evento como é feito no método “executar“. Deu para perceber que escutar um evento é algo muito complicado :). Nesse caso não faremos nada com o objeto init que é o que representa o evento, mas no decorrer do exemplo veremos como isso funciona.

No nosso exemplo, nós realizamos um saque de 200, depois olhamos o saldo, depositamos 300 e olhamos o saldo de novo. Parece bobo (e não vou negar isso), mas é útil para testarmos os diferentes escopos. Pois quando mudamos os scopos para stateless (default, sem a anotação @ApplicationScoped) percebemos que a conta sempre volta a ter o valor inicial.

Nesse exemplo estou trabalhando com classes, mas para trabalhar com interfaces o procedimento é o mesmo, ao ver que você pediu para injetar um objeto tipado por uma determinada interface a CDI vai procurar por uma implementação da mesma, e encontrando injeta. Agora se ela encontrar nenhuma ou mais de uma, aí será lançada uma excessão. “Poxa, mas e se eu tiver mais de uma implementação no bean package”? Palma, palma, palma, não priemos cânico, vamos chegar a ver isso.


Manipulação de eventos


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package br.com.jugms.weldse.model;
 
import javax.enterprise.event.Event;
import javax.inject.Inject;
 
/**
* @author http://gilliard.eti.br
*/
public class CaixaEletronico {
 
    @Inject private Event<Movimentacao> eventMovimentacao;
 
    public void depositar(float valor)
    {
        eventMovimentacao.fire(new Movimentacao(valor));
    }
    public void sacar(float valor)
    {
        eventMovimentacao.fire(new Movimentacao(-1 * valor));
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package br.com.jugms.weldse.model;
 
/**
* @author http://gilliard.eti.br
*/
public class Movimentacao {
 
    private float valor;
 
    public Movimentacao(float valor) {
        this.valor = valor;
    }
 
    public float getValor() {
        return valor;
    }
}

Como podemos ver, a classe CaixaEletronico não tem nenhuma anotação para “ativá-la” como injetável, e ainda assim ela é injetada no nosso objeto da classe MovimentacaoControl. Podemos ver também que nossa classe CaixaEletronico não debita diretamente da nossa conta (que nesse exemplo simplista só existe uma). O CaixaEletronico envia uma mensagem que é representada pela classe Movimentacao. Para disparar esse evento nosso CaixaEletronico pede para que seja injetado nele um ‘cara’ que sabe disparar eventos do tipo Movimentacao. E então no depósito ele dispara uma movimentação com valor positivo e no saque uma com valor negativo. Bem simples.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package br.com.jugms.weldse.model;
 
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.inject.Named;
 
import br.com.jugms.weldse.intercept.Seguro;
 
/**
* @author http://gilliard.eti.br
*/
@ApplicationScoped
@Seguro
public class ContaBancaria {
 
    @Inject @Named
    private float saldoInicial = 1000.0f;
 
    public void recebeMovimento(@Observes Movimentacao movimentacao)
    {
        saldoInicial += movimentacao.getValor();
    }
 
    public float getSaldo() {
        return saldoInicial;
    }
 
}

Agora na classe ContaBancaria vemos como consumir um evento. Na verdade já tinhamos visto na classe MovimentacaoControl, mas agora vamos de fato utilizar o objeto do evento. Como na MovimentacaoControl, para escutarmos um evento basta termos um método com um parâmetro do tipo do objeto do evento anotado com @Observes. Além disso podemos ver o escopo desse objeto, que é de aplicação. Comente esse escopo e você vai perceber que serão criadas várias contas bancárias, e dessa forma vamos debitar em uma e creditar na outra.

Podemos ver também a injeção de um ‘cara’ do tipo float, mas além de @Inject usamos um @Named para isso. O @Named é um qualificador. Nós também podemos, em vários casos vamos “precisar”, criar nosso próprios qualificadores. Um qualificador serve para especificar melhor qual candidato à injeção queremos que seja escolhido. Seria como uma cláusula “where” na pesquisa por injetáveis. No caso desse float, obviamente algém vai ter que colocá-lo no contexto para que possamos recuperá-lo, e podemos ver isso na classe Produtores.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package br.com.jugms.weldse.model;
 
import javax.enterprise.inject.Produces;
import javax.inject.Named;
 
/**
* @author http://gilliard.eti.br
*/
public class Produtores {
 
    @Produces @Named
    public float getSaldoInicial()
    {
        return 2000.0f;
    }
}

Essa classe agrega todos os métodos produtores da nossa aplicação (que por ser extremamente complexa tem exatamente um :)). Um método produtor (anotado com @Produces) é o oposto do consumidor (@Inject), logo em vez de pegar, ele joga um obejto no contexto, usando como qualificador o @Named que permite darmos um nome ao componente. Só temos que tomar cuidado para não usarmos demais isso e acabarmos tendo tudo ligado via Strings. Nesse exemplo, o nome usado para registrar esse componente é “saldoInicial”, pois segue o padrão java bean.

Podemos ver também que que na classe ContaBancaria deixei um saldo inicial de 1000, e depois mandei injetar nessa propriedade o valor de um componente do tipo float chamado “saldoInicial” que está com valor 2000. Na prática ficará valendo o 2000, pois o 1000 só vai valer antes que as injeções sejam executadas.

Nos próximos posts eu faço uma sessão específica para comentar as possibilidade de injeção e “ejeção” que a CDI provê. E também vou explicar o funcionamento dos interceptadores como podemos ver através da anotação @Seguro da classe ContaBancaria, e também como especificarmos qual implementação de uma interface vamos querer que seja selecionada. Como dá para perceber esse assunto vai longe. Então até o próximo post.

18 thoughts on “Desenvolvendo uma aplicação Desktop com Weld – Parte 1

  1. Pingback: Gilliard Cordeiro » Apresentacao sobre CDI (JSR-299) no Javaneiros2009

  2. @Junior
    Ele não vai conseguir trabalhar com escopos como conversação, request, etc. Basicamente ele trabalha apenas com application, singleton e nada (default = dependent)
    No entanto ele consegue trabalhar bem com interceptadores e decorators, eventos, stereótipos, e por aí vai.
    Para saber exatamente o que entra ou não dê uma olhada aqui e aqui.

  3. Pingback: Gilliard Cordeiro » Desenvolvendo uma aplicação Desktop com Weld – Parte 2

  4. Pingback: Gilliard Cordeiro » Desenvolvendo uma aplicação Desktop com Weld – Final

  5. Só um comentário: acho que onde você disse Juice deveria escrever-se Guice(se você estiver se referindo ao framework da Google).

  6. Boa tarde Gilliard.
    Cara, estou tendo problemas em utilizar o CDI num projeto EAR onde os módulos ficam separados em jars diferentes, você já testou com sucesso um projeto com essa estrutura?
    ear/
    -projeto-entidades.jar
    -projeto-ejbs.jar
    -projeto-client.jar
    -projeto-web.war

    Suponhamos que você deseja injetar um cara que está no projeto-entidades.jar em um determinado ejb no projeto-ejb… Fica dando essa exception :
    Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Injection point has unsatisfied dependencies.
    Parabéns pelo blog, quando tiver um tempo vem tomar um café aqui com o pessoal da
    NF-e.
    Abraços.

  7. Valeu @Gaspar
    Olha, eu uso uma estrutura parecida mas com Seam. Com CDI ainda não usei essa estrutura mas deve funcionar. Você tem o beans.xml no entidades.jar?

    E pior que to precisando ir aí mesmo pegar umas dicas de GlassFish, com o perdão do trocadilho nesse AS sou um peixe fora d’água hehehe.

  8. @Gilliard Cordeiro
    Ahauhauhauhauhauhauaha, vem sim, o glassfish 3.01 apresenta melhorias frente ao 2.1.1, já estamos usando para desenv e em breve entrará em produção.
    Quanto ao beans.xml eu tenho no projeto sim… É sinistro, se eu coloco pra injetar um cara que está dentro do ejb vai normal, mas se for de uma dependencia (um jar no caso) ele volta a lançar o erro.
    Tô achando que o uso do CDI é focado para o Profile Web…
    Se conseguir alguma coisa te aviso.

  9. @Gilliard Cordeiro
    Exato, eu já vi sim, por enquanto na net a única coisa que achei e que está de acordo com o meu problema foi esse forum, e a solução alcançada foi unir todos os projetos em um só….

  10. Obrigado, Gilliard,
    pela sua excelente didática.
    Faz parecer fáceis conceitos complexos para um iniciante em CDI.
    Voce vem provar que existem autores que explicam as tecnologias de forma simples, enquanto outros, mesmo com boa vontade, complicam na textualização.

  11. Pingback: Um time poderoso : JSF + CDI + Observers + Events | paulsidewalk.com.br

  12. Pingback: Um time poderoso : JSF 2.x + CDI + Observer + Event | paulsidewalk.com.br

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>