Injeção de Dependência e Gerenciamento de Contextos no Java EE 6

Propaganda
artigo
CDI
Injeção de Dependência e Gerenciamento
de Contextos no Java EE 6
Alessandro Lazarotti
([email protected]) Instrutor e consultor na divisão
JBoss da Red Hat Brasil. Trabalha com Java Corporativo
desde 2003 envolvido com arquitetura, análise, P&D
de soluções baseadas em open source e lecionando
treinamentos e workshops sobre tecnologia em geral.
á algum tempo na plataforma Java, a utilização de frameworks
que proveem uma implementação do pattern Dependency
Injection (DI), (http://martinfowler.com/articles/injection.
html#InversionOfControl) tem sido uma prática comum em
vários projetos. Baixo acoplamento entre objetos, facilidade na criação
de testes, códigos mais limpos são algumas das muitas vantagens na
utilização deste pattern. Porém, por não serem soluções baseadas em
especificações, cada framework possui sua maneira própria de trabalhar
com DI, sendo muitas vezes incompatíveis entre si. No JavaEE 6, com a
padronização da injeção de dependência, seu poder é levado ao extremo podendo ser utilizado de maneira intercambiada entre classes java
convencionais (POJOs) e componentes como Servlets, EJBs e Managed
Beans. Agora é possível injetar um EJB em uma classe java simples, entre
outras funcionalidades.
J
0SFTQPOTÈWFMQPSFTUBOPWJEBEFÏB+43i$POUFYUTBOE%FQFOEFODZ
Injection for the JavaTM EE platform”, ou simplesmente CDI. Esta nova
especificação dita a interação entre os tradicionais componentes JavaEE
e POJOs em uma aplicação, além de definir o ciclo de vida de tais instâncias através da declaração de escopos para seus objetos gerenciados.
Uma entre as mais aguardadas
novidades dentro da nova
plataforma JavaEE 6 é a
especificação CDI. Trazendo
ideias de frameworks, como
Google Guice e JBoss Seam, o CDI
promete revolucionar o modelo
atual de desenvolvimento de
sistemas corporativos. Este
artigo apresentará, por meio
de exemplos, os principais
aspectos desta especificação.
CDI também é responsável por:
t 1FSNJUJSRVFRVBMRVFSVNEFTFVTPCKFUPTHFSFODJBEPTTFKBNBDFTsíveis diretamente por uma interface JSF ou JSP através de Unified
Expression Language (EL).
t 0GFSFDFSOPWPNFDBOJTNPEFEFDMBSBÎÍPEFJOUFSDFQUBEPSFTGSBDBmente acoplados.
t )BCJMJEBEFEFiEFDPSBSwPCKFUPTJOKFUBEPT
t 6NNPEFMPEFOPUJmDBÎÍPEFFWFOUPT
t 6NOPWPDPOUFYUPXFCTPNBOEPBP3FRVFTU"QQMJDBUJPOF4FTsion, chamado de Conversation.
t 'PSOFDFSVNDPOKVOUPEF41*4FSWJDF1SPWJEFS*OUFSGBDFQBSBQPTsibilitar extensões portáveis.
A implementação de referência desta especificação é mantida pela divisão JBoss da Red Hat , sob o nome Weld, e está hospedado no mesmo
domínio do Seam Framework (ver em referências).
Vamos começar a explorar os aspectos do CDI por sua base, a definição
de um objeto gerenciável por esta especificação, o “Bean”.
35
"SUJHPt$%*o*OKFÎÍPEF%FQFOEÐODJBF(FSFODJBNFOUPEF$POUFYUPTOP+BWB&&
Você precisa de “Beans”
Para habilitar o CDI em uma aplicação ou serviço JEE 6, deve-se inserir no
projeto o arquivo: META-INF/beans.xml. Este arquivo, mesmo vazio, é o
que será rastreado por um servidor de aplicação para associar os serviços
do CDI com o pacote implantado, seja jar, war ou ear. Uma vez tendo uma
aplicação CDI, todos os “beans” do projeto passam a ser gerenciados pelo
servidor de aplicação. Mas o que é de fato um bean? O JavaEE 6 define
dois tipos de beans passíveis de serem manipulados pelo CDI: Managed
Beans e Session Beans.
Managed Beans
Diferente do que possa parecer pela semelhança na nomenclatura, essa categoria de bean não tem algo haver com JSF. No CDI, um bean do tipo Managed
Bean é qualquer classe java que:
t 4FKBDPODSFUBPVBOPUBEBDPN!%FDPSBUPS
t 5FOIB QFMP NFOPT VN DPOTUSVUPS TFN QBSÉNFUSP PV BOPUBEP
DPN!*OKFDU
t /ÍPTFKBVN&+#
t /ÍPTFKBVNB+1"&OUJUZ
t /ÍPTFKBVNBDMBTTFJOUFSOB
t /ÍPJNQMFNFOUFBJOUFSGBDFKBWBYFOUFSQSJTFJOKFDUTQJ&YUFOTJPO
Como pode ser observado, a maioria dos POJOs podem ser encarados como
sendo Managed Beans, sendo que nenhuma anotação extra é necessária para
tal. Uma vez encontrado o arquivo beans.xml, qualquer classe java com as
características acima passam a ser gerenciadas pelo CDI.
A nova especificação define também que qualquer Managed Bean
suporta a implementação dos métodos de callback a seu ciclo de vida,
DPNP !QPTU$POTUSVDU JOWPDBEP BQØT JOJDJBMJ[BS P CFBO
F!1SF%FTUSPZ
(invocado antes da instância do bean ser destruída). Um servlet container,
como o Tomcat, não é obrigado por especificação a possuir suporte a CDI,
mas implementações desta especificação, como o Weld, possuem extensões
que possibilitam o deploy neste tipo de ambiente. A principal diferença de
uma aplicação CDI em um servlet container ou em um servidor de aplicação
JavaEE 6, é que no último os beans do tipo Managed Beans podem se valer de
RVBMRVFSUJQPEFJOKFÎÍPEFSFDVSTPTDPNP!1FSTJTUFODF$POUFYU!3FTPVSDF
!&+#FEFNBJTBOPUBÎÜFTQBSB%*EFDPNQPOFOUFT+BWB&&FNTFVTBUSJCVUPT
enquanto nos servlet containers você pode não se valer, por exemplo, de
serviços como injeção de contexto de persistência. O novo “web profile”,
exigido pela especificação JavaEE 6 para servidores de aplicações, possui
suporte a CDI.
Dependency Injection
0 $%* USBCBMIB DPN %* BUSBWÏT EF BOPUBÎÜFT EFmOJEBT QFMB +43
i%FQFOEFODZ*OKFDUJPOGPS+BWBwWFSNBJTEFUBMIFTTPCSFB+43OP
RVBESPi3FMBÎÍP FOUSF B +43 F +43w
6N FYFNQMP EF DPNP Ï
SFBMJ[BEBBJOKFÎÍPEFEFQFOEÐODJBDPN$%*FTUÈOB-JTUBHFN
-JTUBHFN*NQMFNFOUBÎÍPTJNQMFTEFJOKFÎÍPEFEFQFOEÐODJB
public class RelatorioGerencial{
...
}
public class RelatorioService{
private @Inject RelatorioGerencial relatorioGerencial;
public void gerarRelatorio()
{
// implementação
}
}
Nesta primeira listagem, é injetado o bean RelatorioGerencial no bean
3FMBUPSJP4FSWJDFBUSBWÏTEBBOPUBÎÍP!*OKFDU*NQPSUBOUFOPUBSRVFOÍP
foi preciso inserir nenhuma anotação de classe especial, já que por definição ambas as classes obedecem ao padrão “managed beans” do CDI.
Toda DI na especificação é baseada nos tipos dos beans. No exemplo da
-JTUBHFNPÞOJDPCFBORVFDPSSFTQPOEFBPUJQP3FMBUPSJP(FSFODJBMGPJ
injetado. Isso é diferente de frameworks como o JBoss Seam até sua verTÍPYOBRVBMBJOKFÎÍPÏCBTFBEBFN4USJOHTPVOPOPNFEBWBSJÈWFM
BRVBMFTUÈTFOEPSFBMJ[BEBBJOKFÎÍP"-JTUBHFNUSB[PVUSPFYFNQMP
também válido para injetar RelatorioGerencial em RelatorioService.
-JTUBHFN%*CBTFBEBOBJOUFSGBDF
public class RelatorioGerencial
implements Relatorio {
...
}
public class RelatorioService{
Session Beans
private @Inject Relatorio relatorio;
Session Beans são componentes gerenciados pelo servidor de aplicação definidos pela especificação Enterprise JavaBeans (ver nesta edição o artigo mais
HFSBMTPCSF+BWB&&PVOBFEJÎÍPPBSUJHPTPCSF&+#
MPHPRVFNEFmOF
seu gerenciamento de estado é sua especificação e não o CDI. Contudo, todas
as funcionalidades expostas para um bean do tipo Managed Bean também é
válida para o bean do tipo Session Bean, como injeção de dependência, notificação de eventos, CDI Interceptors e CDI Decorators.
public void gerarRelatorio(){
Uma vez que tanto Managed Beans quanto Session Beans são considerados
“CDI Beans”, um pode ser injetado ou responder a eventos disparados pelo
outro, sem qualquer distinção ou restrição.
36 www.mundoj.com.br
// implementação
}
}
Nesta listagem, RelatorioGerencial também é injetado em RelatorioService, uma vez que ele implementa a interface Relatório. É correto afirmar
que RelatorioGerencial é um tipo de: (a) Object (de maneira implícita,
QFMBQSØQSJBQMBUBGPSNB+BWB
C
3FMBUPSJPFD
3FMBUPSJP(FSFODJBM
"SUJHPt$%*o*OKFÎÍPEF%FQFOEÐODJBF(FSFODJBNFOUPEF$POUFYUPTOP+BWB&&
Desta forma, ele é elegível para qualquer tentativa de injeção na qual um
destes tipos é o alvo a ser injetado.
Restrigindo tipos
O desenvolvedor pode optar por restringir apenas qual tipo da sua hierarquia poderá ser elegível para alguma injeção. Isso é realizado através
EBBOPUBÎÍP!5ZQFE7FKBB-JTUBHFN
Listagem 3. Restringindo o bean ao tipo RelatorioGerencial.
@Typed(RelatorioGerencial.class)
public class RelatorioGerencial
implements Relatorio {
...
}
Restringindo o tipo do bean, apenas alvos a serem injetados que correspondem com exatidão aquele tipo receberão sua instância, independentemente de sua classe pai ou interface. Desta forma, o bean RelatorioGerencial, como é declarado na Listagem 3, não poderia ser injetado em
3FMBUPSJP4FSWJDFEB-JTUBHFNQPJTJTTPSFTVMUBSJBFNVNFSSPFNUFNQP
de deployment.
Resolvendo ambiguidades com @Qualifers
4FPCFBOOÍPQPTTVJSFTUSJÎÜFTEFUJQPDMBTTFBOPUBEBDPN!5ZQFE
F
existe mais de um bean que satisfaz um mesmo alvo de injeção, podemos
ter outro problema. A Listagem 4 ilustra essa situação.
Listagem 4. Tipos ambíguos.
public class RelatorioGerencial
implements Relatorio {
...
}
public class RelatorioFuncional
implements Relatorio{
...
}
2VBM EFTUFT CFBOT TFSJB JOKFUBEP FN 3FMBUPSJP4FSWJDF EB -JTUBHFN RVF
requer algum Relatorio? Na verdade, nenhum, pois um erro em tempo de deployment mencionando: “Injection point has ambiguous dependencies” será
lançado. Para o CDI poder realizar a resolução de qual bean deve ser injetado, o
desenvolvedor deve fornecer no momento da injeção uma segunda anotação
para desfazer a ambiguidade. A base para essa anotação é uma implementaÎÍPEF!2VBMJmFS0DØEJHPEB-JTUBHFNFYJCFDPNPDPOTUSVJSVNBBOPUBÎÍP
para dizer qual o bean correto a ser aplicado em determinada injeção.
Na realidade todo bean que não possui um qualificador, ou seja, não
QPTTVJ TPCSF TJ VNB BOPUBÎÍP EP UJQP 2VBMJmFS DPNP P !(FSFODJBM FN
3FMBUPSJP(FSFODJBMEB-JTUBHFNQPTTVJJNQMJDJUBNFOUFVN2VBMJmFS!
%FGBVMU4FNQSFRVFÏVUJMJ[BEP!*OKFDUFMFQSPDVSBBVUPNBUJDBNFOUFVN
UJQPEBRVFMFCFBODPNPRVBMJmDBEPS!%FGBVMUTFWPDÐOÍPEFmOJSFYQMJDJUBNFOUFPVUSPWFS-JTUBHFN
-JTUBHFN6UJMJ[BÎÍPEF!2VBMJmFS
/*
* Criando anotação Gerencial, do tipo Qualifier
*/
@Qualifier
@Retention(RUNTIME)
public @interface Gerencial {}
/*
* Criando anotação Funcional, do tipo Qualifier
*/
@Qualifier
@Retention(RUNTIME)
public @interface Funcional{}
/*
* Definindo um bean como Gerencial
*/
@Gerencial
public class RelatorioGerencial
implements Relatorio {
...
}
/*
* Definindo um bean como Funcional
*/
@Funcional
public class RelatorioFuncional
implements Relatorio {
...
}
/*
* Resolvendo ambiguidade
*/
public class RelatorioService{
private @Inject @Gerencial Relatorio relatorio;
public void gerarRelatorio(){
// implementação
}
}
Mais sobre a CDI Dependency Injection
Como pode ser observado, a especificação CDI define uma maneira bem
controlada de resolução de tipos a serem injetados nas dependências
de um bean. O nome ostentado pela especificação quanto ao algoritmo
utilizado é “Typesafe Resolution”. A influência direta por tal mecanismo
tipado para DI é o framework Google Guice, e as vantagens são muitas:
prevenção contra erros de injeção em tempo de execução, apoio de ferramentas IDE para produtividade, clareza nos códigos, entre outros.
De maneira geral, a injeção é realizada de três formas: injeção por atributo, por método set ou por construtor. Em qualquer um dos modos,
é possível utilizar qualifiers para resolver ambiguidades na injeção. A
Listagem 6 exemplifica os tipos de injeção.
37
"SUJHPt$%*o*OKFÎÍPEF%FQFOEÐODJBF(FSFODJBNFOUPEF$POUFYUPTOP+BWB&&
WMFU3FRVFTU-JTUFOFSPV"TZOD-JTUFOFSEVSBOUFJOWPDBÎÜFTEFRVBMRVFS
+&&8FC4FSWJDFTEVSBOUFJOWPDBÎÜFTSFNPUBTBRVBMRVFS&+#EVSBOUF
BFYFDVÎÍPEFVNNÏUPEPEFUJNFPVUEF&+#5JNFSTEVSBOUFBFOUSFHB
de mensagens para algum EJB MDB. É de visibilidade exclusiva a cada
cliente do contexto, não podendo, portanto, compartilhar o estado de
suas instâncias com múltiplos clientes. A duração do contexto atrelado a
este escopo é de apenas uma requisição.
!4FTTJPO4DPQFE DPOUFYUP BUJWP B QBSUJS EF VNB TPMJDJUBÎÍP BP
método“service()” de qualquer Servlet ou “doFilter()” de qualquer servlet
'JMUFSRVBOEPPDPOUBJOFSFYFDVUB)UUQ4FTTJPO-JTUFOFS4FSWMFU3FRVFTtListener ou AsyncListener. Assim como RequestScoped, tem visibilidade
exclusiva a cada cliente. A duração do contexto atrelado a este escopo é
a mesma da sessão web de um usuário, podendo ser destruída por seu
timeout ou por chamada a httpSession.invalidate().
!"QQMJDBUJPO4DPQFEÏDSJBEPQPSRVBMRVFSTFSWMFUSFRVFTUJOWPDBÎÜFT
de web service, invocações remotas e assíncronas de EJB, execução de
EJB timeouts, entregas de mensagens JMS e execução de listeners
como ServletContextListener, HttpSessionListener, AsyncListener ou
ServletRequestListener. Este escopo é compartilhado por todas as
threads de uma aplicação, ou seja, todos os usuários têm a mesma
visibilidade do estado de objetos sobre este escopo. O contexto para
este escopo é destruído quando a aplicação se encontra em estado
indisponível.
!$POWFSTBUJPO4DPQFEFTUFDPOUFYUPÏEFmOJEPJOJDJBMNFOUFQBSBRVBMquer request JSF, porém pode ser extendido através da CDI SPI (Service
Provider Interface) para outros frameworks web. O contexto Conversation geralmente é encarado como um meio-termo entre Session e Request, podendo armazenar o estado dos beans entre múltiplas requisições, exclusiva ao cliente, mas por um período menor do que a sessão. Tal
período pode ser definido por timeout ou por demarcação programática
da vida útil deste contexto, através da invocação dos métodos “conversation.begin()” e “conversation.end()”. A Listagem 8 exibe um bean em
escopo de conversação com estas demarcações.
Listagem 6. Tipos de injeção.
private @Inject Relatorio relatorio;
...
//injeção por método set
@Inject
public void setRelatorio(Relatorio relatorio) {
t
this.relatorio = relatorio;
}
...
//injeção por construtor
@Inject
public RelatorioService(Relatorio relatorio) {
t
this.relatorio = relatorio;
}
//injeção por construtor usando qualifiers
@Inject
public RelatorioService(@Gerencial Relatorio relatorioGerencial,
@Funcional Relatorio relatorioFuncional) {
t
this.relatorioGerencial = relatorioGerencial;
this.relatorioFuncional = relatorioFuncional;
}
Beans contextuais
O desenvolvedor de aplicações web está acostumado a lidar com escopos request, session ou application. Chamamos o conjunto de objetos
associados a estes escopos de contexto ou, em inglês, Context. No CDI,
todo bean está necessariamente dentro de algum contexto, que delimita
sua visibilidade para o resto da aplicação (incluindo os aspectos relacionados à DI). A Listagem 7 exibe um bean em escopo de sessão.
Listagem 8. Bean no escopo Conversation.
@ConversationScoped @Stateful
public class RelatorioService{
private @Inject @Gerencial Relatorio relatorio;
private @Inject Conversation conversation;
// outros atributos
Listagem 7. Bean no escopo de sessão.
@SessionScoped
public class RelatorioService{
public void gerarRelatorio( .. ){
conversation.begin();
// implementação
}
private @Inject @Gerencial Relatorio relatorio;
public void gerarRelatorio(){
// implementação
}
public void downloadRelatorio( .. ){
// implementação
conversation.end();
}
}
Sempre que ocorrer a alteração de estado de um bean, isso se reflete em seu
contexto. Todos que possuírem uma referência a este mesmo bean através de
DI pelo seu escopo, terá este seu novo estado na instância. O CDI especifica
quatro tipos de escopos:
t !3FRVFTU4DPQFEDPOUFYUPBUJWPBQBSUJSEFVNBSFRVJTJÎÍPQPEFOEP
ser ela: invocações ao método“service()” de qualquer Servlet ou “doFilUFS
wEFRVBMRVFSTFSWMFU'JMUFSRVBOEPPDPOUBJOFSFYFDVUBBMHVN4FS38 www.mundoj.com.br
@Remove
public void destroy() {}
}
"SUJHPt$%*o*OKFÎÍPEF%FQFOEÐODJBF(FSFODJBNFOUPEF$POUFYUPTOP+BWB&&
No exemplo da Listagem 8, um bean do tipo Stateful SessionBean foi utilizado
com o escopo Conversation. Quando for executado o método gerarRelatorio(),
o estado deste bean será mantido no contexto de conversação independentemente do número de requisições posteriores. Ao executar downloadRelatorio(), o estado do objeto será descartado, então o próprio CDI irá invocar
JNQMJDJUBNFOUFPNÏUPEPEFTUSPZ
BOPUBEPDPN!3FNPWFEFTUSVJOEPEFmnitivamente a instância do Session Bean. O nome dado na especificação para
este tipo de comportamento em uma conversação, demarcado pelas fronteiras
conversation.begin() e conversation.end() é “Long-Running Conversation”. Esta
é uma rara situação na qual o desenvolvedor é capaz de controlar o ciclo de
vida de um bean. Nos outros contextos, a gerência do estado de uma instância
e sua destruição é dada sempre pelo container.
Produzindo Beans
-JTUBHFN$SJBOEPCFBOTDPN!1SPEVDFT
public @SessionScoped class Configuracao {
private TipoDeRelatorio tipoDeRelatorio;
...
public @Produces @Configurado Relatorio obtemRelatorio(){
switch (tipoDeRelatorio) {
case GERENCIAL: return new RelatorioGerencial();
Embora um Session Bean seja utilizado nesta listagem, também poderia ser
VN.BOBHFE#FBO/FTUFDBTPOÍPQSFDJTBSJBFYJTUJSVNNÏUPEPDPN!3FNPve (mas lembre-se, o desenvolvedor não poderá se valer de serviços exclusivos
a EJBs como thread pooling, chamada assíncrona, passivação gerenciada por
um servidor de aplicação etc. ).
Se a demarcação conversation.begin() não for definida em algum método,
como em gerarRelatorio() da Listagem 8, a conversação será do tipo transiente
e irá durar o ciclo de apenas um request.
case FUNCIONAL: return new RelatorioFuncional();
default: return null;
}
}
}
@RequestScoped
public class RelatorioService{
private @Inject @Configurado Relatorio relatorio;
O “pseudo” escopo Dependent
Como já mencionado, todo bean CDI é contextual. Mesmo se não for definido
explicitamente um escopo para o objeto, ele receberá de forma implícita o chaNBEPQTFVEPFTDPQP!%FQFOEFOU6NCFBOEFTUFUJQPQPTTVJTFVDPOUFYUPF
ciclo de vida dependente do escopo do objeto a qual ele é injetado, quando
VNÏDSJBEPPPVUSPUBNCÏNÏ/B-JTUBHFNUFOEPFNWJTUBPTUJQPTQBESÜFT
do CDI, é correto afirmar que um objeto do tipo RelatorioGerencial é um manaHFECFBODPNRVBMJmFS!%FGBVMUFQTFVEPFTDPQP!%FQFOEFOU
Porém, mesmo se um bean possuir um escopo definido, ainda sim é possível
injetá-lo de forma dependente ao contexto de outro bean, através da anotação
!/FX7FKBVNFYFNQMPEFDPNPJTTPQPEFTFSGFJUPOB-JTUBHFN
public void gerarRelatorios(){
// implementação
}
}
-JTUBHFN6TPEF!1SPEVDFTDPN%*OPTQBSÉNFUSPT
public @SessionScoped class Configuracao {
private TipoDeRelatorio tipoDeRelatorio;
...
public @Produces @Configurado Relatorio obtemRelatorio(
RelatorioGerencial gerencial,
RelatorioFuncional funcional){
switch (tipoDeRelatorio) {
-JTUBHFN6UJMJ[BOEPQTFVEPFTDPQPBUSBWÏTEF!/FX
@Gerencial @SessionScoped
public class RelatorioGerencial
implements Relatorio {
...
}
case GERENCIAL: return gerencial;
case FUNCIONAL: return funcional;
default: return null;
@RequestScoped
public class RelatorioService{
}
private @Inject @Gerencial Relatorio relatorio;
private @Inject @New @Gerencial Relatorio novoRelatorio;
public void gerarRelatorios(){
// implementação
}
}
/B -JTUBHFN B DMBTTF 3FMBUPSJP4FSWJDF QPTTVJ EPJT CFBOT EP UJQP 3FlatorioGerencial, um no contexto session e outro (novoRelatorio) no
contexto request, por possuir escopo dependente.
}
}
Como já descrito, chamamos de bean no CDI classes Java simples, com as restrições definidas no começo deste artigo, além dos Session Beans. Entidades
JPA que possuem ciclo de vida gerenciado por um contexto de persistência ou
classes da própria API da linguagem Java, como coleções, não são por natureza
beans. Contudo, é possível expormos qualquer tipo de classe como beans conUFYUVBJTEP$%*BUSBWÏTEBBOPUBÎÍP!1SPEVDFT"MÏNEFQSPNPWFSWBSJBEPT
UJQPTEFPCKFUPTBTFSCFBOT!1SPEVDFTUBNCÏNQPEFTFSVUJMJ[BEPDPNPVNB
39
"SUJHPt$%*o*OKFÎÍPEF%FQFOEÐODJBF(FSFODJBNFOUPEF$POUFYUPTOP+BWB&&
ferramenta poderosa de polimorfismo, mudando o comportamento de sua
BQMJDBÎÍPFNUFNQPEFFYFDVÎÍP7FKBVNFYFNQMPOB-JTUBHFN
"BOPUBÎÍP!$POmHVSBEPEB-JTUBHFNÏEPUJQP2VBMJmFSBTTJNDPNP!
(FSFODJBMF!'VODJPOBMEB-JTUBHFN&TUF2VBMJmFSÏVUJMJ[BEPQBSBSFTPMWFSB
!*OKFDUFN3FMBUPSJP4FSWJDFVUJMJ[BOEPPNÏUPEP1SPEVDFTPCUFN3FMBUPSJP
como um método fábrica.
Um detalhe nesta listagem é que as implementações de Relatório são geradas
a partir do operador java “new”, e não pelo CDI. Desta forma, no momento que
são instanciados não é possível utilizar interceptadores CDI ou realizar algum
tipo de injeção em seus construtores. Para driblar isso, podemos utilizar injeção
JNQMÓDJUBOPTNÏUPEPTDPN!1SPEVDFT7FKBPNFTNPNÏUPEPPCUFN3FMBUPSJP
EB-JTUBHFNNPEJmDBEPQBSBVTPEFJOKFÎÍPJNQMÓDJUBOB-JTUBHFN
Outra forma de expor rapidamente um objeto como managed bean, é
BUSBWÏTEFBUSJCVUPTBOPUBEPTDPN!1SPEVDFT0FGFJUPÏPNFTNPEP
RVFOPTNÏUPEPT"-JTUBHFNUSB[VNB4USJOHFTUÈUJDBTFOEPQSPNPWJEBBCFBO$%*FNFTDPQP"QQMJDBUJPOBUSBWÏTEF!1SPEVDFT
0SFTQPOTÈWFMQPSJOKFUBSVNCFBOBTFSEFTUSVÓEPÏBBOPUBÎÍP!%JTQPTFT"
DMBTTFRVFDPOUÏNVN!1SPEVDFTRVFQSPEV[EFUFSNJOBEP#FBOQPEFQPTTVJS
NÏUPEPTDPNQFMPNFOPTVNEFTFVTBSHVNFOUPTDPNBBOPUBÎÍP!%JTQPTFT
BmNEFFODFSSBSSFDVSTPTDPNPFTUÈSFQSFTFOUBEPOB-JTUBHFN
Nesta listagem, o método encerrar é chamado automaticamente pelo
CDI quando a conversação associada ao bean EntityManager com qualimFS!3FMBUPSJP%BUB#BTFGPSFODFSSBEB*TTPPDPSSFQPSDPOUBEBBOPUBÎÍP
!%JTQPTFTFNVNEFTFVTBSHVNFOUPT
Interceptors
Interceptors são classes que disponibilizam um pré-processamento que
anteveem a execução de um método de um bean, de forma automática.
Geralmente estes pré-processamentos (ou pós) são comuns a várias
instâncias, o que chamamos de interesses transversais de comportamento. O maior dos problemas nas aplicações não-CDI é que o modelo de
Interceptor padrão na plataforma Java EE 5 é intrusivo. Veja um exemplo
EFVNJOUFSDFQUPSEP+BWB&&OB-JTUBHFN
-JTUBHFN6TPEF!1SPEVDFTFNBUSJCVUPT
-JTUBHFN6UJMJ[BÎÍPEFJOUFSDFQUPSTFN$%*
public @SessionScoped class Configuracao {
private @Produces @ApplicationScopped @Desenvolvedor static
String dev = “Lazarotti”;
public class LoggerInterceptor {
Logger logger = ...
...
@AroundInvoke
public Object logar(InvocationContext context) {
logger.debug(“ Executando o metodo “+ context.getMethod());
return context.proceed();
}
Destruindo Beans
}
}
-JTUBHFN'JOBMJ[BOEPBQSPQSJBEBNFOUFCFBOTDPN!%JTQPTFT
public class RelatorioEntityManager {
@PersistenceUnit(unitName=”RelatorioDataBase”) EntityManagerFactory emf;
@Stateless
@Interceptors(LoggerInterceptor.class)
public class RelatorioService {
...
@Produces @ConversationScoped @RelatorioDataBase
public EntityManager criar() {
public void gerarRelatorios(){
// implementação
}
return emf.createEntityManager();
}
}
public void encerrar(@Disposes @RelatorioDataBase
EntityManager entityManager) {
return emf.createEntityManager();
}
}
Um bean sempre é destruído pelo CDI. Quando um contexto se encerra, como
um final de request ou de sessão, por exemplo, os bens são finalizados apropriadamente pelo ciclo de vida CDI. Contudo, quando um bean é inicializado
QPS!1SPEVDFTQPEFNPTEFmOJSVNPVNBJTNÏUPEPTQBSBTFSFYFDVUBEPOP
momento em que o bean é destruído, assim recursos alocados na criação da
instância podem ser encerrados de maneira correta nestes.
40 www.mundoj.com.br
Nesta listagem todos os métodos de RelatorioService serão interceptados por
LoggerInterceptor, incluindo gerarRelatorios(). Um problema visível nesta tradicional implementação é que a classe RelatorioService está atrelada de forma
muito forte a LoggerInterceptor, o que pode dificultar tarefas como codificação
de testes. Inspirado no modelo de interceptadores presentes no framework
JBoss Seam, o CDI traz em sua especificação um modelo menos acoplado para
a codificação, chamado de Interceptor Bindings, além de fornecer um modo
TJNQMFTQBSBIBCJMJUBSPVOÍPPVTPEPTNFTNPT"-JTUBHFNUSB[PNFTNP
DØEJHPEB-JTUBHFNQPSÏNVUJMJ[BOEP*OUFSDFQUPS#JOEJOHT
Note que em nenhum momento é colocado de forma explícita a utilização de
LoggerInterceptor para RelatorioService.
Todos interceptors no CDI são por padrão desabilitados. Para habilitar um
interceptor é necessário incluir a classe Interceptor dentro do arquivo beans.
YNMDPOGPSNF-JTUBHFN
"SUJHPt$%*o*OKFÎÍPEF%FQFOEÐODJBF(FSFODJBNFOUPEF$POUFYUPTOP+BWB&&
A ordem em que os interceptors aparecem no arquivo beans.xml é a ordem
de execução dos mesmos. Para remover a execução de um interceptador para
determinado ambiente (de testes, por exemplo), basta removê-lo do arquivo.
-JTUBHFN6UJMJ[BÎÍPEFJOUFSDFQUPSDPN$%*
/*
* Criando anotação Logged, do tipo InterceptorBinding
*/
@InterceptorBinding
@Retention(RUNTIME)
public @interface Logged{}
deste aspecto, um Decorator conhece detalhes do objeto a ser interceptado
(decorado) e sua implementação é dependente dos dados em tempo de
execução deste objeto, podendo, com base nisso, lhe dar novas características
de forma dinâmica. No CDI, a especificação dá um nome para o alvo a ser interceptado por um Decorator: Delegate Injection Point. Para melhor entender os
UFSNPTEBFTQFDJmDBÎÍPWBNPTWFSPFYFNQMPEB-JTUBHFN
-JTUBHFN6UJMJ[BÎÍPEF%FDPSBUPST
public class RelatorioService {
private @Inject Relatorio relatorio;
public void gerarRelatorios(){
// implementação
}
public @Interceptor @Logged class LoggerInterceptor {
Logger logger = ...
@AroundInvoke
public Object logar(InvocationContext context) {
logger.debug(“ Executando o metodo ”+ context.getMethod());
return context.proceed();
}
}
@Decorator
public class RelatorioServiceDecorator {
private @Inject @Delegate RelatorioService relatorioService;
private @Inject Emailservice correio;
}
public void gerarRelatorios(){
if(!ehUmdiaUtil(relatorioService.getRelatorio().getData()) ){
correio.enviarMensagem(“ Relatorio “ +
relatorioService.getRelatorio().getNome() +
” sendo gerado fora do expediente ”).para(ADMIN);
}
// continuar com a execução
relatorioService.gerarRelatorio();
}
@Stateless @Logged
public class RelatorioService {
...
public void gerarRelatorios(){
// implementação
}
}
}
-JTUBHFN)BCJMJUBOEP*OUFSDFQUPS#JOEJOHTFNCFBOTYNM
<beans xmlns=”http://java.sun.com/xml/ns/javaee”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee http://java.sun.
com/xml/ns/javaee/beans_1_0.xsd”>
<interceptors>
<class>br.com.mundoj.cdiapp.SecurityInterceptor</class>
<class>br.com.mundoj.cdiapp.LoggerInterceptor</class>
</interceptors>
</beans>
Decorators
Decorator é um padrão de projeto catalogado no livro Design Pattern: Elements of Reusable Object-Oriented Software, de quatro conhecidos autores
quase sempre referenciados como Gang Of Four, ou simplesmente GoF (veja
na edição anterior uma excelente entrevista com esta“gangue”). Como objetivo
deste padrão, está descrito: “Atribuir responsabilidade adicionais a um objeto
dinamicamente”, é exatamente isso que o CDI proporciona ao desenvolvedor,
com sua implementação de Decorator. A diferença entre um Decorator e um
Interceptor é que os interceptadores são para regras comuns a vários objetos
da aplicação, como: segurança, transação, log e demais serviços de infraestrutura não dependentes de regras de negócio de um objeto específico. Diferente
Neste exemplo, RelatorioService está sendo decorado por RelatorioService%FDPSBUPSBOPUBEPDPN!%FDPSBUPS$BTPBTPMJDJUBÎÍPQBSBHFSBÎÍPEFVN
relatório seja feita fora de um dia útil, uma mensagem é enviada para algum
administrador do sistema. Veja que um objeto Decorator, assim como os Interceptors, são beans CDIs comuns, logo podem usufruir de qualquer tipo de
injeção, assim como podem ser definidos um contexto para si. Se nenhum for
definido, ele terá escopo dependente do escopo do bean a ser decorado. Uma
das injeções em um Decorator é obrigatória, é o que se trata de Delegate
*OKFDUJPO1PJOUOPTTPPCKFUPBMWP/PDBTPEB-JTUBHFNBBOPUBÎÍP!
Delegate em RelatorioService, define que ele é o bean a ser interceptado.
4ØQPEFFYJTUJSVNÞOJDPBUSJCVUPJOKFUBEPDPNBEFNBSDBÎÍP!%FMFHBUF
em um Decorator.
Um Decorator deve ter a assinatura de método idêntica ao método que se deTFKBSiEFDPSBSwBTTJNDPNPÏNPTUSBEPOB-JTUBHFN1BSBUBMVN%FDPSBUPS
pode opcionalmente se valer de implementar a mesma interface do objeto
decorado, ou na falta de uma interface, estender o bean e sobrescrever o método a interceptar. Pode ser processado qualquer trecho de código no método
EFDPSBEPJODMVTJWFTFSEFTDPOUJOVBEBTVBFYFDVÎÍP"JOEBOB-JTUBHFN
veja que o código comentado com “//continuar a execução” faz uma chamada
explícita ao método do bean decorado, isso garante que o fluxo da execução
no Decorator continue para aquela instância de bean. Com sua omissão, quando invocado o método “gerarRelatorio()”, sua execução iria até o Decorator e
nunca seria chamado de fato o método que está no bean.
41
"SUJHPt$%*o*OKFÎÍPEF%FQFOEÐODJBF(FSFODJBNFOUPEF$POUFYUPTOP+BWB&&
Diferente dos Interceptors, um Decorator é definido para tipos não-ambíguos.
Quando é definido um Delegate Injection Point, este terá que possuir todos os
Qualifiers necessários para resolver as possíveis ambiguidades e caso isso não
ocorra uma exception será lançada informando ambiguidade. Se for preciso
interceptar chamadas generalizadas a tipos de beans, utilize os Interceptors.
Assim como os Interceptors, os Decorators precisam ser declarados em beans.
YNMQBSBTFSFNIBCJMJUBEPT7FKBOB-JTUBHFNDPNPJTTPÏGFJUP
-JTUBHFN)BCJMJUBOEP%FDPSBUPSTFNCFBOTYNM
-JTUBHFN6UJMJ[BOEPFWFOUPT
@Qualifier
@Target({FIELD, PARAMETER})
@Retention(RUNTIME)
public @interface Pronto {
}
public class RepositorioDeRelatorios {
public void adicionar(@Observes @Pronto Relatorio relatorio){
...
}
<beans xmlns=”http://java.sun.com/xml/ns/javaee”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee http://java.sun.
com/xml/ns/javaee/beans_1_0.xsd”>
<decorators>
<class>br.com.mundoj.cdiapp.RelatorioServiceDecorator</class>
</decorators>
</beans>
Eventos
Embora a DI permita um código com baixo acoplamento, sem o instanciamento explícito de um objeto no código, às vezes ainda acabamos por ter que
compor um objeto com outro, sem que na modelagem este relacionamento
GBÎBBMHVNTFOUJEP"-JTUBHFNFYFNQMJmDBFTUFUJQPEFEFQFOEÐODJB
}
@Stateless @Remote(Email.class)
public class EmailService implements Email{
@Asynchronous
public void mensagemParaRelatorios(@Observes @Pronto Relatorio
relatorio){
enviar(“O relatorio requisitado esta concluído e disponível no ftp”)
.para(relatorio.getUsuario().getEmail());
}
}
public class RelatorioService {
private @Inject Relatorio relatorio;
@Inject @Pronto Event<Relatorio> relatorioEvent;
public void gerarRelatorio(){
// implementação
...
relatorioEvent.fire(relatorio);
}
-JTUBHFN$PNQPTJÎÍPQBSBBQSPWFJUBNFOUPEFTFSWJÎPT
public class RelatorioService {
}
private @Inject Relatorio relatorio;
private @Inject RepositorioDeRelatorios repositorio;
private @Inject Email correio;
public void gerarRelatorio(){
// implementação
...
repositorio.adicionar(relatorio);
correio.enviarMesagem(“O relatorio requisitado esta concluído e
disponível no ftp”)
6NBOPWBDPOEJÎÍPQBSBPFTUBEPEFVNSFMBUØSJPGPJEFmOJEBQFMPRVBMJmFS!
Pronto. Tanto RepositorioDeRelatorios como EmailService possuem observadores de eventos, para quando um relatório atingir o estado de pronto realizar
BMHVNBBÎÍP*TTPÏGFJUPBUSBWÏTEBBOPUBÎÍP!0CTFSWFTFNTFVTNÏUPEPT
Deve-se injetar a interface “javax.enterprise.event.Event”, seguida de um
qualifer do evento na classe responsável por executar a ação a ser capturada
OB-JTUBHFNBDMBTTF3FMBUPSJP4FSWJDF
"JOUFSGBDF&WFOUQPTTVJPNÏUPEP
fire(Event) que de fato irá disparar o evento.
.para(relatorio.getUsuario().getEmail());
}
Acessando beans em páginas web
}
Na listagem, RelatorioService possui um repositório de relatórios que pode
armazenar o relatório gerado em um ftp, e então mandar um mensagem de
e-mail notificando o usuário que seu relatório está disponível. Porém, para alguns arquitetos, a dependência do bean RepositorioDeRelatorios e Email pode
não parecer o melhor dos designs para este código, isso poderia ser evitado.
Outro ponto é que o método gerarRelatorio() da classe RelatorioService está
com responsabilidades demais. Uma solução elegante neste caso seria aplicar
um evento dizendo que o relatório está pronto, e fazer com que observadores
a este evento realizem suas ações específicas de forma automática. A Listagem
FYJCFBNFTNBTPMVÎÍPVUJMJ[BOEPFWFOUPT
42 www.mundoj.com.br
É possível acessar beans CDI de qualquer meio que suporte a notação Java
de Expression Language (EL), como JSP e JSF. Em aplicações JavaServer
Faces, mesmo os beans do tipo EJB Session Beans podem ser acessados
diretamente pelas páginas, sem ter a necessidade da criação de classes
+BWB JOUFSNFEJÈSJBT BOPUBEBT DPN !.BOBHFE#FBOT PV EFDMBSBEBT OP
BSRVJWPGBDFTDPOmHYNMWFSOFTUBFEJÎÍPBSUJHPTPCSF+4'
Para acessar qualquer bean CDI em uma página, é necessário anotá-la
DPN!/BNFE$POUVEPFTTBBOPUBÎÍPOÍPUFNPNFTNPFGFJUPRVFB
TFNFMIBOUF!/BNFEPGSBNFXPSL+#PTT4FBNSFTQPOTÈWFMQPSUPSOBS
VNBDMBTTFRVBMRVFSFNVNDPNQPOFOUF4FBN
/P$%*!/BNFEUPSOB
o bean acessível por EL, todas demais features da especificação, como DI,
"SUJHPt$%*o*OKFÎÍPEF%FQFOEÐODJBF(FSFODJBNFOUPEF$POUFYUPTOP+BWB&&
contextos, interceptors, decorators e outros, continuam existindo mesmo
TFNUBMBOPUBÎÍP4FOFOIVNOPNFGPSBEJDJPOBEPB!/BNFEPOPNF
via EL será o mesmo nome do bean, porém iniciando com a primeira
MFUSB FN NJOÞTDVMB /B -JTUBHFN 3FMBUPSJP4FSWJDF Ï FYQPTUP QBSB
uma página JSF. Se não fosse definida a String “gerenciadorRelatorio” na
BOPUBÎÍPNBJTBJOEBTJNBDMBTTFGPTTFBOPUBEBDPN!/BNFEPBDFTTP
OBQÈHJOBTFSJBBUSBWÏTEF\SFMBUPSJP4FSWJDFHFSBS3FMBUPSJP^
-JTUBHFN/PNFBOEPVNCFBOQBSBBDFTTP&@Named(“gerenciadorRelatorio”)
public class RelatorioService {
private @Inject Relatorio relatorio;
public void gerarRelatorio(){
// implementação
}
}
<h:commandButton action=”#{gerenciadorRelatorio.gerarRelatorio}”
value=”Gerar Relatorio” immediate=”true”/>
Agrupando características de um bean
-JTUBHFN$SJBOEPVN4UFSFPUZQF
@ConversationScoped
@Transactional
@Logged
@Stereotype
@Target(TYPE)
@Retention(RUNTIME)
public @interface DAO {}
@DAO
public class RelatorioDao extends BaseDao {
@Override
public void salvar(Relatorio relatorio){
// implementação
}
}
Facilmente pode ser identificado em sistemas de 'n' camadas, tipos de objetos
que apresentam características semelhantes. Geralmente essas características
são associadas a responsabilidades comuns a objetos da mesma camada.
Alguns exemplos disso são beans que atuam nas páginas JSF, que sempre posTVFN!/BNFEQBSBFTUBSEJTQPOÓWFJTFNQÈHJOBTFOBTVBNBJPSJBFTUÍPOP
FTDPQP!3FRVFTU4DPQFE#FBOTRVFJOUFSBHFNDPNBDBNBEBEFQFSTJTUÐODJB
possuem seus métodos transacionais se valendo de Interceptors Bindings
além de poderem conter segurança associada a sua execução, entre muitos
outros exemplos. O CDI oferece em sua especificação a possibilidade de unir as
características comuns dos beans, através da definição de Stereotypes.
Um Stereotype pode conter vários Interceptors, um escopo padrão, nomeação
para acesso via EL, a definição se o bean a qual é aplicado possui implementaÎÍPBMUFSOBUJWBFBJOEBUFSVNDPOKVOUPEFPVUSPT4UFSFPUZQFT/B-JTUBHFN
é criado um Stereotype DAO e aplicado a um bean.
A especificação traz por padrão uma implementação de Stereotype chamado Model para ser utilizado em web frameworks MVC. A forma como
GPJDPOTUSVÓEPFTVBTDBSBDUFSÓTUJDBTFTUÍPOB-JTUBHFN
-JTUBHFN04UFSFPUZQF.PEFM
@Named
@RequestScoped
@Stereotype
@Target({TYPE, METHOD, FIEL})
@Retention(RUNTIME)
public @interface Model {}
Deployments alternativos
O CDI permite que o desenvolvedor trabalhe com implementações alternativas de seus beans, podendo selecionar as versões apropriadas no momento de
deployment. Isso pode ser útil em ambientes de desenvolvimento nos quais
nem todos os recursos podem estar disponíveis ao sistema, como integração a
serviços de terceiros, ou mesmo para realizar tarefas associadas a testes.
Para construir uma versão alternativa de um objeto, você deve: estender a
implementação original do bean, sobreescrever os métodos a serem reimQMFNFOUBEPTBOPUBSBDMBTTFDPN!"MUFSOBUJWFFIBCJMJUBSFTTBWFSTÍPFN
CFBOTYNM"-JTUBHFNFYFNQMJmDBFTUFTQBTTPT
Porém, se o bean a ser substituído no deployment da aplicação possuir
RVBMJmFST BQFOBT B BOPUBÎÍP !"MUFSOBUJWF OÍP JSÈ DPOTFHVJS TVCTUJUVJS B
instância do bean em todo o sistema. Isso acontece porque um bean alternativo não herda todos os metadados de sua classe pai. Para que um segundo
bean substitua completamente outro bean, além de estendê-lo ele tem que
possuir suas anotações. Uma maneira simples de fazer isso é através da anoUBÎÍP!4QFDJBMJ[FTRVFUSB[QBSBBDMBTTFmMIBUPEPTPTRVBMJmFSTEBDMBTTF
pai. Outro ponto que pode ser incômodo na definição de Alternatives, é a
declaração de cada um em beans.xml. Esta tarefa pode ser simplificada com
a criação de Stereotypes, assim pode-se habilitar ou desabilitar um grupo
JOUFJSPEFCFBOTBMUFSOBUJWPT"-JTUBHFNFYJCFBVUJMJ[BÎÍPEFVNCFBO
que utiliza Stereotype e Specializes.
Considerações finais
Como pode ser notado, as novidades são muitas e afetam diretamente o
design e comportamento das aplicações como: aumento da testabilidade de
sistemas ou serviços, diminuição do acoplamento entre soluções, aumento da
expressividade do código, simplificação da comunicação entre componentes
da plataforma JavaEE, diminuição de problemas em runtime através da
Typesafe Resolution etc. De modo geral, da persistência à apresentação, toda a
plataforma JEE 6 é afetada diretamente por esta especificação.
Cada um dos tópicos da especificação apresentados neste artigo podem ser explorados com mais profundidade pelo desenvolvedor que deseja trabalhar com
$%*"MFJUVSBEBFTQFDJmDBÎÍP+43ÏTJNQMFTFDPNQPTUBEFWÈSJPTDØEJHPT
cobrindo todos os aspectos da tecnologia. Além desta leitura, recomendo o download da implementação de referência Weld, que possui aplicações-exemplos
que podem ser utilizadas como ponto de partida para um projeto CDI. Ainda
sobre Weld, sua documentação é bem completa e pode ser uma excelente fonte
de consulta para o desenvolvedor. Alguns Maven Archetypes para iniciar um
QSPKFUP$%*DPNP8FMEKÈFTUÍPEJTQPOÓWFJTWFKBSFGFSÐODJBT
t
43
"SUJHPt$%*o*OKFÎÍPEF%FQFOEÐODJBF(FSFODJBNFOUPEF$POUFYUPTOP+BWB&&
-JTUBHFN*NQMFNFOUBÎÍPBMUFSOBUJWBEFVNCFBO
Da costura a solda – do Seam ao Weld
@Alternative
public class RelatorioServiceMock extends RelatorioService {
@Override
public void gerarRelatorio(){
System.out.println(“Sendo executado RelatorioService alternativo, “ +
“nenhum relatorio sendo gerado”)
}
}
beans.xml
<beans xmlns=”http://java.sun.com/xml/ns/javaee” xmlns:xsi=”http://www.
w3.org/2001/XMLSchema-instance” xsi:schemaLocation=”http://java.sun.
com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd”>
<alternatives>
<class>br.com.mundoj.cdiapp.RelatorioServiceMock</class>
<class>br.com.mundoj.cdiapp.EmailServiceMock</class>
<class>br.com.mundoj.cdiapp.ContasWebserviceMock</class>
...
</alternatives>
</beans>
-JTUBHFN#FBOBMUFSOBUJWPDPN4UFSFPUZQFF4QFDJBMJ[FT
@Alternative
@Stereotype
@Target(TYPE)
@Retention(RUNTIME)
public @interface Mock {}
@Specializes @Mock
public class RelatorioServiceMock extends RelatorioService {
@Override
public void gerarRelatorio(){
System.out.println(“Sendo executado RelatorioService alternativo, “ +
“nenhum relatorio sendo gerado”)
}
}
beans.xml
<beans xmlns=”http://java.sun.com/xml/ns/javaee” xmlns:xsi=”http://www.
w3.org/2001/XMLSchema-instance” xsi:schemaLocation=”http://java.sun.
com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd”>
<!-- somente e necessario indicar o stereotype, nao as classes -->
<alternatives>
<class>br.com.mundoj.cdiapp.Mock</class>
</alternatives>
</beans>
44 www.mundoj.com.br
Alguns frameworks se destacaram na comunidade Java na implementação de DI como, por exemplo: PicoContainer http://www.
picocontainer.org/, Spring http://www.springsource.org/about,
Google Guice http://code.google.com/p/google-guice/, entre
outros. Com a grande aceitação destes frameworks e do emprego
de injeção de dependência, o JCP resolveu incorporar no JavaEE
5 estas mesmas ideias em seu modelo de componentes gerenciados por um servidor de aplicação (Servlets, EJBs, JSF Managed
Beans, DataSources etc.). Desta forma, através da especificação,
ficou possível injetar qualquer componente gerenciado, em outro
componente gerenciado através de anotações.
Embora tenha sido um grande avanço na especificação, isso
ainda não solucionava o problema de quem trabalha com classes
simples, POJOs, que não eram EJBs, e ainda assim precisavam
de um recurso como injeção de dependência. Estes desenvolvedores teriam que recorrer a frameworks de terceiros para obter
essa funcionalidade. Outro agravante é que mesmo utilizando
estes frameworks, a integração entre seus POJOs e modelos de
componentes JavaEE 5 não eram possíveis de se beneficiarem
mutuamente de injeção de dependência, ou seja, não era possível
injetar um componente EJB em uma classe simples e vice-versa
de maneira natural.
Visto este gap entre a especificação e aplicações do dia-a-dia
desenvolvidas por milhares de pessoas, Gavin King, criador do
popular Hibernate e desenvolvedor na JBoss, divisão da Red Hat,
começou o desenvolvimento de um framework onde os seus
componentes pudessem interagir tanto com componentes gerenciados pelo contêiner (como, por exemplo, os EJBs), quanto componentes de classes java convencionais. Além disso, tal framework
também integraria qualquer um de seus componentes (sejam
POJOs simples ou não) com o modelo padrão da especificação
para desenvolvimento de aplicações web, o JavaServer Faces,
dispensando a criação e declaração de JSF Managed Beans. A este
framework, Gavin King lhe deu o nome de Seam, que significa
“costura” que é justamente o que o framework se propõe a fazer
integrando os mais diversos aspectos da especificação com facilidades necessárias para o desenvolvimento de softwares em Java.
'PJEJTQPOJCJMJ[BEPQBSBEPXOMPBEBWFSTÍPEP4FBNFNKVOIP
EF 0 4FBN USB[JB DPOTJHP BT GBDJMJEBEFT FN UPEP BTQFDUP
relacionado à DI para quase todos tipos de objeto, assim como ser
pioneiro na ideia de modelagem de instâncias contextuais.
No mesmo mês da liberação do Seam, Gavin King alavanca uma
nova JSR junto ao JCP a fim de trazer os benefícios do framework
para a especificação JavaEE. A requisição de Gavin foi catalogada
DPNP +43 DPN P OPNF EF 8FC#FBOT IUUQKDQPSHFO
KTSTVNNBSZ JE 0 OPNF EB FTQFDJmDBÎÍP UFWF BMHVNBT
mudanças até sua versão final hoje chamada oficialmente de
“Contexts and Dependency Injection for the JavaTM EE platform”,
o que de fato faz mais sentido uma vez que é possível a utilização
de seus componentes mesmo sem a aplicação interagir com a
web. Em paralelo a sua aprovação no JCP, a JBoss trabalhou em
"SUJHPt$%*o*OKFÎÍPEF%FQFOEÐODJBF(FSFODJBNFOUPEF$POUFYUPTOP+BWB&&
uma implementação para a especificação, também iniciada sobre
o nome WebBeans, mas hoje chamada de Weld (solda).
Após uma série de discussões sobre possíveis conflitos entre a
+43$%*FB+43%*WFSNBJTEFUBMIFTTPCSFB+43OP
RVBESPi3FMBÎÍPFOUSFB+43F+43w
BFTQFDJmDBÎÍPGPJ
aprovada e hoje é parte integrante da plataforma JavaEE 6. Weld,
por sua vez, é a implementação de referência desta especificação.
Além de implementar o CDI como um todo, Weld também traz
extensões portáveis construídas sob a SPI da especificação, como
suporte a Wicket para estrutura MVC web, possibilidade de executar em ambientes JavaSE puros como aplicações Desktop, assim
como em simples containers servlets.
Pelo fato do Weld implementar várias ideias presentes no Seam
Framework, e ambos serem mantidos pelo time JBoss da Red
Hat, alguns desenvolvedores podem estar em dúvida sobre a
continuidade ou futuro dos projetos, mas o fato é que os dois
projetos coexistirão. Seam 3 irá possuir como mecanismo de
DI e gerenciamento de contextos o Weld, porém manterá seus
aspectos de integração com as ferramentas não-standards da
plataforma JavaEE, como geração de PDFs, geração de planilhas,
escopo baseado em jBPM, Identity Management, envio de e-mail
via facelet, integração com GWT, integração com Drools, apoio de
ferramentas de desenvolvimento (seam-gen, jboss tools), integração com Hibernate, componentes JSFs customizados e qualquer
outra adição não-padrão da plataforma.
Para migração do Seam e seu mecanismo atual de DI e componenUFT DPOUFYUVBJT 4FBN Y
QBSB B OPWB JNQMFNFOUBÎÍP CBTFBEB
no Weld e suas anotações (Seam 3.x), existe um projeto chamado
Seam Bridge que permitirá a utilização de beans CDI juntamente
com o antigo modelo de componentes do framework, garantindo
a portabilidade entre as soluções.
Referências
t +43$%*IUUQKDQPSHFOKTSEFUBJM JE
t +43%*IUUQKDQPSHFOKTSEFUBJM JE
t +43
WFSTVT
+43
IUUQSFMBUJPOUP#MPHHFST
CommentsOnAnnotationsForDependencyInjection
t $%*3FGDBSEIUUQSFGDBSE[E[POFDPNSFGDBSE[DPOUFYUTBOEEFQFODFODZ
t 8FMEP$%*3*IUUQTFBNGSBNFXPSLPSH8FME
t 8FME o .BWFO "SDIFUZQFT IUUQTFBNGSBNFXPSLPSH%PDVNFOUBUJPO
8FME2VJDLTUBSU'PS.BWFO6TFST
t 4FBN'SBNFXPSLIUUQTFBNGSBNFXPSLPSH
t (PPHMF(VJDFIUUQDPEFHPPHMFDPNQHPPHMFHVJDF
3FMBÎÍPFOUSFB+43F+43
" QPMÐNJDB +43 %FQFOEFODZ *OKFDUJPO GPS +BWB GPJ FTDSJUB
por Bob Lee (criador do Google Guice) e Rod Johnson (criador do
Spring Framework) com o propósito: “Propomos esta JSR para maximizar a reutilização, testabilidade e manutenção do código Java
padronizando um extensível API para injeção de dependência.”
A questão é que tal JSR não padroniza absolutamente nenhum
aspecto comportamental ou qualquer menção de como seria o ciclo de vida em uma implementação baseada nesta especificação.
Os únicos pontos realmente “especificados” na JSR são algumas
BOPUBÎÜFT!*OKFDU!/BNFE!1SPWJEFS!2VBMJmFS!4DPQFF!
Singleton.
Qualquer framework que utilize essas anotações pode dizer que
implementa a especificação. Isso é muito vago e não garante qualquer tipo de comportamento em aplicações que compartilhem de
mesmas anotações, mas com implementações diferentes, ou seja,
QPVDBDPJTBÏQPSUÈWFM"+43o$%*QFMPDPOUSÈSJPQPTTVJVN
comportamento bem definido para todos os aspetos referentes
à DI e contextos, o que deixou muita gente no JCP sem saber a
real necessidade de uma segunda especificação. A SpringSource,
agora sob o nome de VmWare no JCP, se ausentou de todas as
WPUBÎÜFTQBSBB+43PRVFJNQFEJVEFBKVEBSRVFPJNQBTTF
sobre a necessidade ou não desta segunda especificação fosse
SFTPMWJEP $PNP USÏHVB B +43 BDBCPV QPS VUJMJ[BS BT BOPUBÎÜFTEFmOJEBTOB+43BDBMNBOEPPTÉOJNPTOPDPNJUÐ$PN
tudo, a controvérsia sobre as duas especificações ainda é motivo
de grande discussão. Empresas como IBM, Red Hat e Sun fizeram
TFWFSBTDSÓUJDBTFPCTFSWBÎÜFTOPTWPUPTQBSB+43F+43
Para o leitor poder saber sobre o impacto das divergências entre
BTEVBTFTQFDJmDBÎÜFTVNFYFNQMPB!/BNFEQBSB+43TPbretudo, define uma nomeação para um bean ser acessado por EL
em uma página JSP ou JSF (leia neste artigo a sessão “Acessando
CFBOTFNQÈHJOBTXFCw
1BSBB+43!/BNFEEJ[BQFOBTTF
tratar de um critério para injetar um objeto em outro baseado em
VNB 4USJOH DPNPi!*OKFDU !/BNFEQBTTFOHFS
4FBU QBTTFOHFS4FBUw"HPSBVNBQFSHVOUBTFPEFTFOWPMWFEPSTJNQMFTNFOUF
FODPOUSBSVNBDMBTTFBOPUBEBDPN!/BNFETFSÈRVFFMFQPEF
acessar diretamente aquela classe de uma página? A resposta
será: “depende de quem implementa a anotação” – se for o Weld
RVFTFHVFB+43
QPEFNBTTFOÍPGPS
45
Download