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