Implementação de AOP no Spring: Conceitos e Configuração

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>

Tags: Spring AOP java Programação Orientada a Aspectos Proxy Dinâmico

Publicado em 7-14 23:47