Guice + AOP = Interceptor

Tempo de leitura: menos de 1 minuto

Interceptor - Carro de Mad Max em Mad Max – Fury Road (2015)
Interceptor – Carro de Mad Max em Mad Max – Fury Road (2015)

Fala galera, demorei mais voltei. Estou com reforma em casa, isso está consumindo muito meu tempo. Graças a Deus está e a última semana de reforma, aí poderei voltar a me dedicar ao blog. Mas vamos para o que interessa!

Dias a trás fiz dois posts fando sobre o Guice, Injeção de dependência com Guice #1: Introdução e Injeção de dependência com Guice #2: Anotações, o primeiro falando sobre seu objetivo, e o segundo como usar-lo em uma aplicação Java.

Hoje, falarei sobre o uso do Guice em conjunto com o AOP (Programação orientada a aspectos). Essas duas tecnologias quando trabalham juntas, ajudam a remover códigos de infra-estrutura do código de negócio, separando responsabilidades, deixando o código mais legível e simplificado.

Neste post, darei um exemplo de como remover o código do controle de transação. Mas antes de qualquer coisa, uma introdução sobre o que é AOP.

O que é AOP?

De acordo com o Wikipédia:
Em ciência da computação, programação orientada a aspectos ou AOP (Sigla em inglês), é um paradigma de programação de computadores que permite aos desenvolvedores de software separar e organizar o código de acordo com a sua importância para a aplicação (separation of concerns). O programa escrito usando usando orientação a objeto, outro paradigma de programação, não se importa com o comportamento do objeto. O código de comportamento é todo aquele utilizado para implementar funcionalidades secundárias e que encontra-se espalhado por toda a aplicação (crosscutting concern). O AOP permite que esse código seja encapsulado e modularizado.

O conceito foi criado por Gregor Kiczales e a sua equipe na Xerox PARC. Eles desenvolveram o AspectJ, a primeira e mais popular linguagem AOP.

Os paradigmas de programação mais antigos, como a programação procedural e programação orientada a objeto, implementam a separação do código, através de entidades únicas. Por exemplo, a funcionalidade de log de dados, numa linguagem orientada a objetos, é implementada em uma única classe, que é referenciada em todos os pontos onde é necessário fazer log de dados. Como praticamente todo método necessita que alguns dados sejam registrados em log, as chamadas a essa classe são espalhadas por toda a aplicação.

Tipicamente uma implementação de AOP busca encapsular essas chamadas através de uma nova construção chamada de “aspecto”. Um aspecto pode alterar o comportamento de um código pela adição de um comportamento adicional.

Problema

No projeto que trabalho, em vários momento precisávamos garantir que algumas instruções fosse executadas dentro de uma transação, isso é necessário para garantir que o “tudo ou nada”, ou seja, caso algum erro ocorresse, o sistema desfaria tudo o que havia feito até ali, ou efetivar caso nenhum erro fosse lançado. No entanto, como não usávamos nenhum recurso do servidor de aplicação para tratar estas transações, criamos códigos como o exemplo 1.

Exemplo 1:

[code language=”java”]
public <T> T buscarPorId(final Class<T> pClass, final Serializable id) throws Exception {
boolean commitTransaction = false;
final Session s = new HibernateDbUtil().getSession();
// Pesquisa o objeto persistido
final T retorno;
final Transacao transacao = new Transacao();
try {
transacao.begin();
s.clear();
retorno = (T) s.get(pClass, id);
commitTransaction = true;
} catch (final Exception e) {
throw new Exception("Erro ao buscar " + pClass.getName() + " pelo id [" + id + "]", e);
} finally {
if (commitTransaction) {
TransacaoUtil.commit();
} else {
TransacaoUtil.rollback();
}
}
return retorno;
}
[/code]

Solução: Usando AOP

Agora que já passamos por uma introdução do que AOP, e já conhecimentos o Guice, vamos então por a mão na massa.

Nota: A solução de AOP que usamos aqui, foi a AOP Alliance, para mais informações consulte o site do projeto.

Para resolver o problema, criamos três classes:

  • AopModule – esta classe é o módulo Guice.
  • @Transaciton – anotação cuja sua função é “marcar” o método que será transacionado.
  • TransactionInterceptor – esta classe é responsável por transacionar o método.

AopModule

Esta classe tem a responsabilidade de instanciar a classe TransactionInterceptor e executa-la, para cada método no sistema que tiver anotado com a anotation Transaciton.

Código fonte:

[code language=”java”]
public class AopModule extends AbstractModule {
@Override
protected void configure() {
final MethodInterceptor transactionInterceptor = new TransactionInterceptor();
// Qualquer método de qualquer classe que esteja anotado com a anotação @Transaction
bindInterceptor(Matchers.any(), Matchers.annotatedWith(Transaction.class), transactionInterceptor);
}
}
[/code]

@Transaction

Anotação para marcar os métodos que devem ser transacionados.

Código fonte:

[code language=”java”]
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Transaction {
// do nothing
}
[/code]

Importante mencionar que as classe que possuem métodos anotados com @Transaction, deve ser injetadas pelo Guice, caso contrário o objeto não está geranciado por ele, assim o interceptor não será executado.

TransactionInterceptor

Esta classe é o interceptador, o responsável por prove a infra estrutura de transação. Basicamente quando o método é executado, o interceptador é acionado, antes do método, criando uma “capa”, executando o método apenas quando o mesmo for chamado através do método proceed.

Código fonte:

[code language=”java”]
public class TransactionInterceptor implements MethodInterceptor {
private static final Logger logger = Logger.getLogger(TransactionInterceptor.class, ConstantsSfw.IDENTIFICADOR_LOGGER);

public Object invoke(final MethodInvocation invocacao) throws Throwable {
// inicia transacao
final Transacao transacao = new Transacao();
boolean commitTransaction = false;
Method method = null;
try {
transacao.begin();

method = invocacao.getMethod();
final Object retorno = invocacao.proceed();
commitTransaction = true;

return retorno;
} catch (Throwable e) {
String methodName = method != null ? method.getDeclaringClass() + "." + method.getName() + "()" : "";
logger.error("Ocorreu um erro na invocação do método: " + methodName + "\n" + e.getMessage());
throw e;
} finally {
if (commitTransaction) {
transacao.commit();
} else {
transacao.rollback();
}
}
}
}
[/code]

Transacionando um método qualquer

Pronto! A infra já está pronta para ser usada, basta agora anotar os métodos que precisam ser transacionados.

Vejamos abaixo o exemplo

[code language=”java”]
@Transaction
public <T> T buscarPorId(final Class<T> pClass, final Serializable id) throws Exception {
final Session s = new HibernateDbUtil().getSession();
s.clear();
return (T) s.get(pClass, id);
}
[/code]

Importante dizer que o Guice precisar ter visibilidade no métodos anotado para o código a cima funcione, por tanto, caso você venha a anotar um método privado o mecanismo não funcionará.

 

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *