Programação Orientada a Aspectos (AOP)
A Programação Orientada a Aspectos (AOP) é uma abordagem que isola funcionalidades transversais, como autenticação, logging ou controle de transações, em módulos separados. Isso reduz o acoplamento entre componentes e melhora a manutenção do código sem alterar a lógica de negócios original.
Mecanismo Interno do Spring AOP
O Spring implementa AOP usando proxies dinâmicos:
- Proxy baseado em JDK: Aplicável a classes que implementam interfaces, gerando um proxy da interface.
- Proxy baseado em CGLIB: Usado para classes sem interfaces, criando uma subclasse do objeto-alvo.
- O Spring seleciona automaticamente entre JDK e CGLIB, dependendo se a classe implementa interfaces.
Terminologia Essencial do AOP no Spring
- Advice (Conselho): Ação a ser executada, como validação ou logging, definida independentemente.
- JoinPoint (Ponto de Junção): Locais no código onde o advice pode ser aplicado, como antes ou depois de métodos.
- Pointcut (Ponto de Corte): Expressão que seleciona quais JoinPoints serão alvos do advice.
- Aspect (Aspecto): Combinação de advice e pointcut, definindo "o quê" e "onde" aplicar a ação.
- Introduction (Introdução): Adiciona novos métodos ou atributos a classes existentes via aspectos.
- Target (Alvo): Objeto que recebe o advice, focado na lógica de negócios.
- Proxy (Proxy): Objeto gerado pelo Spring para aplicar aspectos ao alvo.
- Weaving (Teia): Processo de aplicar aspectos ao alvo, feito em tempo de execução pelo Spring.
Desenvolvimento AOP com XML no Spring
Configuração inicial inclui adicionar dependências e definir o arquivo XML com namespaces AOP.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
Defina o aspecto e o alvo no contexto Spring, e configure os pointcuts e advice.
<!-- Registrar o serviço alvo -->
<bean id="servicoProduto" class="exemplo.servico.impl.ServicoProdutoImpl"/>
<!-- Registrar a classe do aspecto -->
<bean id="aspectoSeguranca" class="exemplo.aspecto.AspectoSeguranca"/>
<aop:config>
<aop:pointcut id="corteAdicionar" expression="execution(* exemplo.servico.ServicoProduto.adicionar(..))"/>
<aspect ref="aspectoSeguranca">
<aop:before method="verificarPermissao" pointcut-ref="corteAdicionar"/>
</aspect>
</aop:config>
Tipos de Advice
Advice definem ações em diferentes momantos:
- @Before: Executa antes do método alvo.
- @AfterReturning: Após o método, com acesso ao valor de retorno.
- @Around: Envolve o método, permitindo lógica antes e depois.
- @AfterThrowing: Quando o método lança uma exceção.
- @After: Sempre executa, independentemente de exceções.
Exemplo com múltiplos advice em XML:
<aop:config>
<aop:pointcut id="corteListar" expression="execution(* exemplo.servico.ServicoProduto.listar(..))"/>
<aop:pointcut id="corteObterRetorno" expression="execution(* exemplo.servico.ServicoProduto.obterDetalhes(..))"/>
<aop:pointcut id="corteAtualizar" expression="execution(* exemplo.servico.ServicoProduto.atualizar(..))"/>
<aop:pointcut id="corteRemover" expression="execution(* exemplo.servico.ServicoProduto.remover(..))"/>
<aop:pointcut id="corteFinal" expression="execution(* exemplo.servico.ServicoProduto.processar(..))"/>
<aspect ref="aspectoSeguranca">
<aop:before method="verificarPermissao" pointcut-ref="corteListar"/>
<aop:after-returning method="registrarLog" pointcut-ref="corteObterRetorno"/>
<aop:after-returning method="registrarRetorno" pointcut-ref="corteObterRetorno" returning="resultado"/>
<aop:around method="monitorarDesempenho" pointcut-ref="corteAtualizar"/>
<aop:after-throwing method="tratarExcecao" pointcut-ref="corteRemover"/>
<aop:after method="acaoFinal" pointcut-ref="corteFinal"/>
</aspect>
</aop:config>
Classe do aspecto correspondente:
public class AspectoSeguranca {
public void verificarPermissao() {
System.out.println("Validando credenciais...");
}
public void registrarLog() {
System.out.println("Ação registrada no log.");
}
public void registrarRetorno(Object resultado) {
System.out.println("Valor retornado: " + (String) resultado);
}
public void monitorarDesempenho(ProceedingJoinPoint ponto) throws Throwable {
System.out.println("Início do monitoramento...");
ponto.proceed();
System.out.println("Fim do monitoramento...");
}
public void tratarExcecao() {
System.out.println("Exceção capturada: iniciando tratamento.");
}
public void acaoFinal() {
System.out.println("Execução concluída, independentemente de erros.");
}
}
Sintaxe de Expressões Pointcut
Pointcuts usam a expressão execution() para definir padrões de métodos. Formato geral: execution([modificador] retorno pacote.Classe.metodo(parametros)). Exemplo: execution(* exemplo.servico.impl.ServicoProdutoImpl.*(..)) aplica-se a todos os métodos da classe.
Desenvolvimento AOP com Anotações no Spring
Ative o suporte a anotações no XML:
<aop:aspectj-autoproxy/>
Defina a classe do aspecto com anotações:
@Aspect
public class AspectoLogging {
@Before("execution(* exemplo.servico.ServicoProduto.adicionar(..))")
public void validar() {
System.out.println("Verificação antes da adição.");
}
@AfterReturning(value = "execution(* exemplo.servico.ServicoProduto.listar(..))")
public void aposListar() {
System.out.println("Listagem concluída.");
}
@AfterReturning(value = "execution(* exemplo.servico.ServicoProduto.obterDetalhes(..))", returning = "res")
public void aposObter(String res) {
System.out.println("Detalhes obtidos: " + res);
}
@Around("execution(* exemplo.servico.ServicoProduto.atualizar(..))")
public void cicloCompleto(ProceedingJoinPoint jp) throws Throwable {
System.out.println("Pré-atualização...");
jp.proceed();
System.out.println("Pós-atualização...");
}
@AfterThrowing(value = "execution(* exemplo.servico.ServicoProduto.remover(..))", throwing = "erro")
public void erroRemocao(Exception erro) {
System.out.println("Falha na remoção: " + erro.getMessage());
}
@After("execution(* exemplo.servico.ServicoProduto.processar(..))")
public void sempreExecutar() {
System.out.println("Operação finalizada.");
}
}
Registrar beans no XML:
<bean id="servicoProduto" class="exemplo.servico.impl.ServicoProdutoImpl"/>
<bean id="aspectoLogging" class="exemplo.aspecto.AspectoLogging"/>
Implementação AOP via APIs do Spring
Crie classes que implementam interfaces do Spring, como MethodBeforeAdvice ou AfterReturningAdvice.
public class LogAntes implements MethodBeforeAdvice, AfterReturningAdvice {
@Override
public void before(Method metodo, Object[] args, Object alvo) throws Throwable {
System.out.println("Método " + metodo.getName() + " chamado no objeto " + alvo.getClass().getSimpleName());
}
@Override
public void afterReturning(Object retorno, Method metodo, Object[] args, Object alvo) throws Throwable {
System.out.println("Método " + metodo.getName() + " retornou: " + retorno);
}
}
Configurar no XML:
<bean id="servicoProduto" class="exemplo.servico.impl.ServicoProdutoImpl"/>
<bean id="logAntes" class="exemplo.aspecto.LogAntes"/>
<aop:config>
<aop:pointcut id="corteGlobal" expression="execution(* exemplo.servico.impl.ServicoProdutoImpl.*(..))"/>
<aop:advisor advice-ref="logAntes" pointcut-ref="corteGlobal"/>
</aop:config>