Introdução ao Context and Dependency Injection
Veja neste artigo uma introdução à especificação JSR-299, chamada de Context and Dependency Injection (CDI), que introduziu no Java EE uma série de usabilidades e complementos para a arquitetura de aplicações corporativas.
Artigos Java Introdução ao Context and Dependency Injection
Neste artigo apresento o Bernardo S E Vale, que pode ser contatado pelo e-mail bernardosilveiravale@gmail.com. O Bernardo foi meu aluno em sua graduação e está concluindo o último período do curso de Tecnologia em Análise e Desenvolvimento de Sistemas da UTFPR campus Medianeira, neste primeiro semestre de 2013. Atualmente é estagiário no setor de desenvolvimento – TI da empresa Frimesa onde trabalha com diversas tecnologias Oracle, como EBS,Forms,Reports,XML Publisher.
A Especificação JSR 299
Um dos motivos para a adição da especificação CDI foi a necessidade de preencher a lacuna entre Enterprise Java Beans (EJB) no back-end e Java Server Faces (JSF) na camada de Visão. Havia necessidade de uma melhor integração e comunicação entre ambas partes, porém, durante seu desenvolvimento, foi visto que outras usabilidades poderiam ser adicionadas a esta especificação.
O CDI introduziu uma abordagem para melhor interação entre seus componentes (Java EE), com um avançado modelo de ciclo de vida. Entre os principais recursos adicionados pelo CDI ao Java EE:
Integração unificada de Expression Language (EL): Permite que qualquer Bean do CDI possa ser exposto em um componente JSF;
Eventos: CDI permite um mecanismo de troca de notificações (Eventos) Type safe;
Injeção de Dependência: CDI possibilita injeção de Dependência Type safe de qualquer componente Java EE;
Métodos Produtores: Traz uma abordagem para criar injeções polimórficas durante a execução;
Decorators: CDI utiliza o padrão de projeto Decorator para desacoplar questões técnicas da lógica de negócio;
Conversation Scope: CDI adiciona um novo escopo além dos já definidos Request, Application e Session;
Service Provider Interface (SPI): Permite a integração de frameworks third-party no ambiente Java EE.
[1] Definido na especificação Java Interceptors, recurso que cria um meio para interceptar métodos e fazer algum tipo de processamento no mesmo.
Objetivos e Visão Geral do CDI
Um dos focos do CDI é o baixo acoplamento com tipagem forte. Baixo acoplamento torna um sistema mais dinâmico. O sistema pode responder a mudanças de uma maneira bem definida. Alguns recursos do CDI foram definidos justamente para tentar reduzir o acoplamento, os Eventos, interceptadores, produtores e decoradores.
Os eventos gerenciam o contato entre produtores e consumidores de eventos com um mecanismo de notificações, deste modo, a dependência entre estes dois modelos não existe mais.
Interceptadores também são desacopladores, pois podem ser responsáveis por lógicas de negócio técnicas. Exemplificando, pode-se pensar em um conjunto de interceptadores, cada qual tratando uma parte especifica de uma determinada lógica de negócio, dividindo um problema em pequenas partes.
Decoradores permitem que a lógica de negócio possa ser compartimentada, dependendo da necessidade.
Produtores complementam a injeção de dependência de maneira polimórfica, para também permitir que a aplicação tenha o controle da criação de instâncias de um determinado bean.
Frameworks para DI normalmente se orientam por meio de arquivos XML ou de Strings. O CDI traz a abordagem de utilizar as informações de tipagem já contidas nos objetos Java, em adição, utiliza um modelo de programação chamado anotações qualificadoras para determinar relacionamentos de suas diversas funcionalidades, porém, ainda utiliza XML para informar a implantação de alguns facilitadores como, decoradores e interceptadores. Entre as vantagens do type safe, pode-se destacar:
Facilidade dos IDEs para fornecer auto completion, validação, refatoração sem necessidade de ferramentas especiais;
Identificação de objetos, eventos ou interceptadores por meio de anotações - em vez de nomes, facilitando a reutilização e ajudando a descrever qualidades em comum para objetos diferentes;
Facilidade de depuração;
Outra grande funcionalidade concebida através do type safe é a criação de Estereótipos. O CDI permite que uma anotação englobe diversas anotações para definir um modelo, útil quando existem num sistema diversos Bean's com comportamentos semelhantes, evitando códigos repetitivos.
Beans são definidos como objetos gerenciados pelo contêiner com mínimas restrições de programação, também conhecidos pelo acrônimo POJO (Plain Old Java Object). Eles suportam um pequeno conjunto de serviços básicos, como injeção de recurso, callbacks e interceptadores do ciclo de vida [ORACLE 2009].
Com pouquíssimas exceções, quase toda classe Java concreta que possui um construtor com nenhum parâmetro (ou um construtor designado com a anotação @Inject) é um bean [JBOSS, 2012].
Um bean tem a responsabilidade de definir como outros beans dos quais ele depende ou necessita em seu modelo, serão interpretados pelo container, que por sua vez, irá gerenciar o ciclo de vida destes objetos. Ou seja, um bean pode requerer instâncias de outros tipos de beans.
O Ciclo de vida de um bean dependerá do escopo em que ele foi inserido. Por sua vez, os diferentes escopos definem ao container como suas informações serão geridas.
O ciclo de vida de beans é completamente desacoplado no CDI, pois permite que diferentes beans que implementam a mesma interface sejam substituídos uns aos outros, mesmo quando possuem escopos diferentes. De acordo com a especificação JSR 299, um bean possui:
Um conjunto (não vazio) de tipos de bean;
Um conjunto (não vazio) de qualificadores;
Um escopo;
Opcionalmente, um nome EL do bean;
Um conjunto de vinculações com interceptadores;
Uma implementação do bean [JBOSS,2012].
Tipo de Bean
Beans usualmente adquirem referências para outros beans por meio de injeção de dependência. Qualquer atributo injetado especifica um "contrato" que deve ser satisfeito pelo bean para ser injetado [JBOSS,2012].
O tipo de um bean nada mais é que a classe ou interface que o define, por exemplo, num EJB de sessão com visão local, a classe definida na anotação @Local será seu tipo. O tipo de um bean muitas vezes pode ser composto.
De acordo com o código a seguir o tipo de bean é definido pelas classes Aluno e Pessoa e a interface "Criatura", além da classe implícita "Object" herdada pela classe Pessoa.
public class Aluno extends Pessoa implements Criatura{}
Para entender a função de um qualificador, primeiro, observa-se um problema que qualificadores procuram solucionar.
Figura 1: Diagrama de Classes
Observando a Figura 1, na tentativa de injetar a interface "IProcessoPagamento" entra-se num caso específico de ambiguidade, pois, existem múltiplos candidatos para a injeção. Em partes específicas da aplicação pode-se necessitar especificar uma implementação. Qualificadores definem a maneira de fazê-lo sem ferir um dos princípios da engenharia de software "Programe para uma interface e não para uma classe concreta".
Para solucionar este problema, utiliza-se o recurso de anotações Java. Um qualificador é uma anotação Java que por sua vez é anotada como @Qualifier (Listagem 1).
@Target({TYPE, METHOD, PARAMETER, FIELD})
public @interface BoletoQualifier{}
Listagem 1. Anotação Qualificadora
Quando for necessário, por exemplo, obter não apenas um "IProcessoPagamento", mas sim um "Boleto", usa-se a anotação "BoletoQualifier", em dois pontos. Primeiro, na classe concreta "Boleto" (Listagem 2) , segundo, em qualquer ponto de injeção que precisar da classe concreta "Boleto" (Listagem 3). Um bean pode também, utilizar múltiplos qualificadores personalizados.
@BoletoQualifier
public class Boleto implements IProcessoPagamento{}
Listagem 2. Classe concreta Boleto
@Inject @BoletoQualifier
IProcessoPagamento metodo;
Listagem 3. Injetando um Boleto
Built-in Qualifiers
CDI define alguns qualificadores chamados de Built-in Qualifiers, ou seja, qualificadores já criados e definidos. São eles:
Todo bean é anotado como @Any, já a anotação @Default é usada para todo aquele que não possui um qualificador diferente de @Named. Tanto @Any quanto @Default são declaradas implicitamente. Anotando um bean como @Named pode-se definir um nome, o qual definirá este bean em um EL, como no JSF, por exemplo.
A anotação @New permite criar uma nova instância de um bean que deve ser obrigatoriamente um ManagedBean. Esta nova instância não dependerá do seu próprio escopo, e sim, do escopo declarado naquela injeção específica. Ou seja, se uma injeção com escopo de sessão da interface "IProcessoPagamento" for feita e procurar a classe concreta "Boleto" e este, por sua vez estiver com escopo de requisição, por exemplo, a nova instância será um "Boleto" com escopo de sessão.
O escopo de um bean define ao container como funcionará o ciclo de vida daquele objeto, tal como a visibilidade de suas instâncias. No CDI existem quatro escopos pré-definidos:
ConversationScope
Session, Application e Request, funcionam exatamente como são definidos nas aplicações Java EE, porém, estas aplicações não possuem um novo escopo, definido pelo CDI, o ConversationScope.
Este escopo é bem parecido com o Session, pois também mantém o estado associado a um usuário do sistema[1]. Porém, neste escopo, os dados só serão guardados durante a comunicação entre cliente/servidor.
Para entender melhor este escopo pode-se pensar em formulários do tipo Wizard, onde, cede-se informações à aplicação durante várias etapas e as informações cedidas em cada etapa devem ser guardadas enquanto aquele formulário estiver ativo, ou seja, durante aquela tarefa.
Este escopo define uma melhor maneira de guardar dados de um usuário especifico, quando aqueles dados só precisam ser mantidos durante uma determinada tarefa.
Segundo a especificação Java Interceptors, estas classes têm como funcionalidade receptar requisições de métodos de uma classe alvo. CDI traz um conjunto de melhorias para a especificação com uma melhor abordagem, baseada em anotações e semanticamente melhor.
As anotações são usadas para associar um interceptador a um ou mais beans, podendo então, executar diferentes tarefas antes de chamar um método alvo, ou seja, o que foi interceptado. Entre os tipos de interceptação, pode-se dividir entre as seguintes categorias:
Métodos de negócio
Uma interceptação de ciclo de vida é toda aquela que será acionada após algum evento relacionado ao ciclo de vida do bean alvo, como após a construção do mesmo.
As interceptações de métodos de negócio são em nível de método, obviamente, ou seja, um método será marcado para ser interceptado, não um bean. O CDI também permite que uma classe possa interceptar tanto ciclo de vida quanto métodos de negócio.
Interceptação Timeout refere-se a uma especificação do EJB que define temporização para certas chamadas de um EJB, este tipo de interceptação ocorrerá após o estouro deste temporizador.
A nova especificação Java para injeção de dependência muito contribui para criação de aplicações robustas, com fácil integração entre beans com comportamentos diferentes, como EJB e Managed Bean, e melhor controle do ciclo de vida, além de trazer diversos recursos adicionais que contribuem para cumprir seu principal objetivo, baixo acoplamento com tipificação forte.