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.
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 beans, Servlets, JAX-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.
|
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:
http://blog.caelum.com.br/use-cdi-no-seu-proximo-projeto-java/
http://blog.caelum.com.br/4-recursos-novos-do-cdi-1-1/
http://blog.caelum.com.br/4-recursos-novos-do-cdi-1-1/