segunda-feira, 18 de dezembro de 2017

CDI - Injeção de Dependência e Contexto

O que é CDI? Para que serve? E como usar? Bom, essas três perguntas eu pretendo responder nesse post. O CDI é a especificação Java que define a injeção de dependência e inversão de controle para aplicações JavaEE. Como o CDI é uma especificação, existem implementações, e como é comum no Java, existe a implementação de referência, que nesse caso é a Weld.

O que esta especificação define? Basicamente ela trata sobre injeção de dependência (DI) e inversão de controle (IoC) no contexto e escopos de uma aplicação JavaEE. Mas o que é injeção de dependência e inversão de controle? Bom, essas duas coisas andam bem juntas.

Injeção de dependência é tornar disponível a instância de uma classe quando precisamos dela. Isso ocorre quando dentro de uma classe fazemos algo como:

MinhaClasse c = new MinhaClasse();

Nesse momento estamos dizendo que a classe a qual estamos depende da classe MinhaClasse. A questão é que isso é uma coisa corriqueira e que sempre precisamos fazer, então, por quê não tornar isso automático? Aí entra a questão da Inversão de Controle. Vamos passar a responsabilidade de colocar essa instância para o container ao invés de nós ficarmos dando um “new” sempre que quisermos usar uma classe. Dessa forma, passamos a tratar o exemplo acima da seguinte maneira:

@Inject
MinhaClasse c;

Dessa forma, quando o container perceber que precisamos da classe c, ele irá colocar a instancia para nós. Mas para isso funcionar, precisamos aprender umas coisinhas ainda!

Primeiro, para um projeto estar apito a usar o CDI, precisamos adicionar um arquivo de configuração chamado beans.xml que deve ficar dentro de /main/webapp/WEB-INF/. Esse arquivo deve conter o seguinte trecho de código:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
      http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
       version="2.0" bean-discovery-mode="all">

</beans>

Caso seja um módulo EJB ou JAR, o arquivo deve estar no diretório META-INF/. Note que temos esse atributo bean-discovery-mode="all" que informa para o CDI que todas as classes serão consideradas beans CDI. Outras opções para esse valor são: annotated - habilita apenas as classes anotadas - e none que desabilita o CDI. Mas para que a injeção funcione, toda classe que quisermos que seja um bean CDI deve ser pública e possuir um construtor padrão.

No pom.xml devemos colocar:

<!-- Weld (implementacao do CDI) -->
<dependency>
       <groupId>javax.enterprise</groupId>
       <artifactId>cdi-api</artifactId>
       <!-- Use version 2.0 for Weld 3 -->
       <version>2.0</version>
</dependency>

Quando queremos usar um Bean CDI em uma página JSF, precisamos anotar esse Bean com a anotação @Named. Isso permite ao bean CDI ter um nome que pode ser usado pela Expression Language  do JSF. Essa anotação pode receber uma String que dá a classe um nome específico para ser usado nas páginas xhtml. Caso não seja especificado, o nome padrão é o nome da classe com a primeira letra minúscula.

E quando não queremos que determinada classe ou pacote não seja injetada via CDI? Temos algumas formas de fazer isso: Via anotação @Vetoed ou via configuração no arquivo beans.xml.

Usando a anotação temos o seguinte:

@Vetoed
public class MinhaClasse implements MinhaInterface {
         ...
}

Isso fará com que essa classe seja desconsiderada pelo CDI e ela não poderá ser injetada em outras classes. Caso queira que todo o pacote seja desconsiderado, a anotação @Vetoed pode ser usada sobre o pacote:

@Vetoed
package br.com.utils;

public class MinhaClasse implements MinhaInterface {
         ...
}

Via anotação teremos o seguinte:

<beans ... >
<scan>
   <!-- posso excluir apenas a classe: -->
   <exclude name="br.com.utils.MinhaClasse.java"/>
   <!-- ou posso excluir todas as classes do pacote utils: -->
   <exclude name="br.com.utils.*"/>
   <!-- ou posso excluir o pacote util completo, incluindo subpacotes: -->
   <exclude name="br.com.utils.**">
</scan>
</beans>

É possível ainda usar condicionais para definir quando uma classe ou pacote deve ser desconsiderado para injeção usando as TAG’s <if-system-property>, <if-class-available> e <if-class-not-available>, como mostrado a seguir:

<beans ...>
 <scan>
  <exclude name="br.com.rest.*" />
   <exclude name="br.com.faces.**">
    <if-class-not-available name="br.com.MeuBean"/>
   </exclude>
   <exclude name="br.com.dao.*">
    <if-system-property name="propriedade" value="valor"/>
    </exclude>
   <exclude name="br.com.ejb.**">
    <if-class-available name="javax.enterprise.inject.Model"/>
     <if-system-property name="exclude-ejbs"/>
   </exclude>
 </scan>
</beans>

Contexto e Escopos:

O contexto é responsável pelo ciclo de vida e a visibilidade dos beans CDI dentro dos escopos definidos. Os beans CDI tem os seguintes escopos:

·       @RequestScoped – O bean tem o escopo da requisição.
·       @ConversationScoped – O bean com escopo definido pelo desenvolvedor na aplicação.
·   @ViewScoped – do pacote javax.faces.view.ViewScoped, tem o escopo de uma view. Enquanto estiver na mesma tela, o bean estará disponível. Veio para substituir o ViewScoped do JSF.
·    @SessionScoped – O bean tem o escopo da sessão do usuário. É considerado um escopo longo e deve ser usado com cuidado.
·     @ApplicationScoped – O bean fica instanciado durante todo o tempo que a aplicação estiver disponível. É o escopo mais longo e também deve ser usado em poucos casos.

     Fora estes escopos, existe um que é o principal escopo do CDI - @Dependent, digo isto porque quando nenhum escopo é definido explicitamente ele é usado por padrão no CDI (o que geralmente ocorre com a maioria das classes). Este escopo delibera que uma classe quando definida com ele, ao ser injeta em outra classe, terá o mesmo escopo da classe à qual foi injetado. Em outras palavras, quando injetamos uma instância de uma classe com escopo @Dependent dentro de uma instância de uma classe que tenha o escopo de Sessão por exemplo, a classe com o escopo @Dependent passa a ter escopo de Sessão automaticamente.

            Transações Gerenciadas pelo Container

A versão do JTA 1.2  introduz a anotação @javax.transaction.Transactional. Essa anotação possibilita que as aplicações possam demarcar os limites de uma transação declarativamente. Isto é feito utilizando beans CDI gerenciados, assim como as classes definidas como Managed beansServletsJAX-RS e os endpoints JAX-WS.

As anotações podem ser especificadas tanto em nível de método como em nível de classe, no entanto, as anotações em nível de método sobrescrevem aquelas que estão em nível de classe.

Segue o exemplo do uso na classe:

@Transactional
public class MinhaClasse {
   //...
}

Segue o exemplo do uso no método:

public class MinhaClasse {
   public void consultar() {
       //...
   }

   @Transactional
   public void incluir() {
     //...
   }
}

Esse controle de transação é fornecido através de uma implementação de interceptores do CDI que realizam o cancelamento ou recuperação da transação quando necessário.

O elemento TxType da anotação @Transactional fornece tipos de transação equivalente a semântica utilizada nos atributos de transação do EJB. Deve ser especificado da seguinte forma: @Transactional(TxType.REQUIRES_NEW) Segue uma tabela com os tipos suportados:

TxType
Fora de um contexto de Transação
Dentro de um contexto de Transação
REQUIRED (default)
O interceptor deve iniciar uma nova transação JTA, o método de execução do Managed Bean deve então continuar dentro desde contexto de transação e a transação deve ser completada pelo interceptor.
O método de execução do Managed Bean deve continuar dentro deste contexto de transação.
REQUIRES_NEW
O interceptor deve iniciar uma nova transação JTA, o método de execução do Managed Bean deve então continuar dentro deste contexto de transação, e a transação deve ser completada pelo interceptor.
O contexto de transação corrente deve ser suspenso, uma nova transação JTA será iniciada, o método de execução do Managed Bean deve então continuar dentro deste contexto de transação, a transação deve ser completada, e a transação anterior que foi suspensa deve ser retomada.
MANDATORY
Uma TransactionalException com uma TransactionRequiredException aninhada deve ser lançada.
O método de execução do Managed Bean continuará sob o contexto.
SUPPORTS
O método de execução do Managed Bean deve continuar fora de um contexto de transação.
O método de execução do Managed Bean deve continuar dentro deste contexto de transação.
NOT_SUPPORTED
O método de execução do Managed Bean deve continuar fora de um contexto de transação.
O contexto de transação corrente deve ser suspenso, o método de execução do Managed Bean deve continuar fora de um contexto de transação, e a transação anterior suspensa deve ser retomada pelo interceptor que suspendeu essa transação após o método de execução ser completado.
NEVER
O método de execução do Managed Bean deve continuar fora de um contexto de transação.
Uma TransactionalException com uma ValidTransactionException aninhada deve ser lançada.
fonte: Devmedia 

Por padrão, no CDI as exceções checadas não resultam em rollback, como é também no EJB como mostro nessa postagem. Porém instâncias de RuntimeException e suas subclasses resultam em rollback da transação.

Mas assim como no EJB existe uma forma explicita de forçar o rollback nas exceções verificadas, no CDI também é possível fazer. O elemento “rollbackOn” permite que você especifique as classes de exceções que mesmo quando verificadas no bloco try/catch devem fazer o rollback.

@Transactional(rollbackOn={Exception.class})

E como é possível especificar as classes de exceções que forçarão o rollback, também podemos especificar as classes que não devem causar rollback através do elemento “dontRollbackOn”.

@Transactional(dontRollbackOn={IllegalStateException.class})

Para concluir o assunto sobre transações, existe uma anotação que garante que uma única instância de um bean será usada em todo contexto da transação: @TransactionalScoped. Toda classe marcada com essa anotação tem seu ciclo de vida delimitado para a transação JTA ativa no momento. Se durante a transação, múltiplas instâncias desse bean são injetadas em diferentes classes, o CDI refere-se a mesma instância, assegurando que apenas uma instância do bean seja utilizada e que ela mantenha suas propriedades valoradas.

import javax.transaction.TransactionScoped;

@TransactionScoped
public class TestTransactionalScopeBean {
    //...
   

}

       




Bibliografia:




2 comentários: