VRaptor3 – Interceptando recursos anotados
Em primeira lugar, gostaria de tecer meus mais sinceros elogios a equipe VRaptor, a versão 3 esta muito boa, bem mais intuitiva e fácil de usar que a 2.6
Neste artigo vou mostar como interceptar um método de um Resource especifico, identificando-o a partir de uma anotação e executar ações antes do método executar, e após ele executar.
Vamos supor que nós temos o seguinte Resource para adicionar produtos no nosso sistema
-
@Resource
-
public class ProdutoController {
-
private final DaoFactory factory;
-
private final ProdutoDao dao;
-
-
public ProdutoController(DaoFactory factory) {
-
this.factory = factory;
-
this.dao = factory.getProdutoDao();
-
}
-
-
public List<Produto> listar() {
-
return dao.list();
-
}
-
-
public Produto atualizar(Produto produto) {
-
try {
-
factory.beginTransaction();
-
produto = dao.update(produto);
-
factory.commit();
-
return produto;
-
} catch (DaoException ex) {
-
factory.rollback();
-
throw ex;
-
}
-
}
-
-
public Produto adicionar(Produto produto) {
-
try {
-
factory.beginTransaction();
-
produto = dao.store(produto);
-
factory.commit();
-
return produto;
-
} catch (DaoException ex) {
-
factory.rollback();
-
throw ex;
-
}
-
}
-
}
Agora nos queremos que um interceptador intercepte meu recurso, e execute a lógica dentro de um escopo transacional, como fazer isso ? é só criar um interceptador assim.
-
import org.hibernate.Session;
-
import org.hibernate.Transaction;
-
-
import br.com.caelum.vraptor.Intercepts;
-
import br.com.caelum.vraptor.core.InterceptorStack;
-
import br.com.caelum.vraptor.interceptor.Interceptor;
-
import br.com.caelum.vraptor.resource.ResourceMethod;
-
-
@Intercepts
-
public class TransactionInterceptor implements Interceptor {
-
private final Session session;
-
public TransactionInterceptor(Session session) {
-
this.session = session;
-
}
-
Transaction transaction = null;
-
try {
-
transaction = session.beginTransaction();
-
stack.next(method, instance);
-
transaction.commit();
-
} finally {
-
if (transaction != null && transaction.isActive()) {
-
transaction.rollback();
-
}
-
}
-
}
-
public boolean accepts(ResourceMethod method) {
-
return true; //aceita todas as requisições
-
}
-
}
Ok, o interceptador vai rodar e abrir transação antes e depois de executar a logica, e os métodos transacionais da minha lógica irão se reduzir a isto
-
public Produto atualizar(Produto produto) {
-
return dao.update(produto);
-
}
-
-
public Produto adicionar(Produto produto) {
-
return dao.store(produto);
-
}
Ok mas, neste caso temos o problema de que métodos que não exigem transação estão abrindo e fechando transação a cada requisição sem necessidade.
Como então selecionar apenas algumas lógicas para serem transacionais ? podem criar uma anotação para isto, desta forma:
-
import java.lang.annotation.ElementType;
-
import java.lang.annotation.Retention;
-
import java.lang.annotation.RetentionPolicy;
-
import java.lang.annotation.Target;
-
-
/**
-
* Usado para garantir que um determinado recurso interceptado seja executada em um
-
* escopo de tranzação.
-
* @author Tomaz Lavieri
-
* @since 1.0
-
*/
-
@Retention(RetentionPolicy.RUNTIME)
-
@Target({ElementType.METHOD,ElementType.TYPE})
-
public @interface Transactional {}
Agora precisamos marcar os pontos onde queremos que o escopo seja transacional com esta anotação.
-
@Resource
-
public class ProdutoController {
-
private final ProdutoDao dao;
-
-
public ProdutoController(ProdutoDao dao) {
-
this.dao = dao;
-
}
-
-
public List<Produto> listar() {
-
return dao.list();
-
}
-
-
@Transactional
-
public Produto atualizar(Produto produto) {
-
return dao.update(produto);
-
}
-
-
@Transactional
-
public Produto adicionar(Produto produto) {
-
return dao.store(produto);
-
}
-
}
Ok o código ficou bem mais enxuto, mas como interceptar apenas os métodos marcados com esta anotação ?? para tal basta no nosso accepts do TransactionInterceptor verificarmos se a anotação esta presente no método, ou no proprio rescurso (quando marcado no recurso todos os métodos do recurso seriam transacionais).
A modificação do método ficaria assim
-
public boolean accepts(ResourceMethod method) {
-
return method
-
.getMethod() //metodo anotado
-
.isAnnotationPresent(Transactional.class)
-
|| method
-
.getResource() //ou recurso anotado
-
.getType()
-
.isAnnotationPresent(Transactional.class);
-
}
Pronto agora somente os métodos com a anotação @Transacional são executados em escopo de transação e economisamos linhas e linhas de códigos de try{commit}catch{rollback throw ex}