10 - Aspect Oriented Programming (Spring AOP)
O que é Programação Orientada a Aspectos (AOP)?
A Programação Orientada a Aspectos (AOP) é um paradigma de programação que ajuda a separar as preocupações transversais da lógica de negócio principal de uma aplicação.
Preocupações transversais são funcionalidades que afetam múltiplas partes da aplicação, tais como:
- Logging (Registro de logs)
- Segurança
- Gerenciamento de Transações
- Monitoramento de Performance
- Auditoria
- Tratamento de Exceções
Sem AOP, o mesmo código precisaria ser repetido em várias classes, tornando a aplicação mais difícil de manter.
O Spring AOP permite aos desenvolvedores adicionar essas funcionalidades sem modificar diretamente o código de negócio.
Por que usar AOP?
Sem AOP
@Service
public class PaymentService {
public void processPayment() {
System.out.println("Starting method...");
// Business Logic
System.out.println("Ending method...");
}
}
O código de logging está misturado com a lógica de negócio.
Com AOP
@Service
public class PaymentService {
public void processPayment() {
// Business Logic
}
}
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))")
public void logBefore() {
System.out.println("Starting method...");
}
}
A lógica de negócio permanece limpa enquanto o logging é tratado separadamente.
Terminologias em AOP
Entender os principais conceitos de AOP é essencial antes de implementar aspectos.
Aspect
Um Aspect é um módulo que encapsula uma preocupação transversal.
Exemplos:
- LoggingAspect
- SecurityAspect
- MonitoringAspect
- AuditAspect
@Aspect
@Component
public class LoggingAspect {
}
Join Point
Um Join Point representa um ponto específico durante a execução de um programa onde um aspecto pode ser aplicado.
No Spring AOP, os join points são tipicamente:
- Execução de método
- Execução de construtor (suporte limitado)
- Tratamento de exceções
Exemplo:
paymentService.processPayment();
A execução de processPayment() é um join point.
Advice
Um Advice define qual ação deve ser executada e quando.
Spring oferece vários tipos de advice:
| Advice | Descrição |
|---|---|
| @Before | Executa antes de um método |
| @After | Executa depois de um método |
| @AfterReturning | Executa após uma execução bem-sucedida |
| @AfterThrowing | Executa quando ocorre uma exceção |
| @Around | Executa antes e depois de um método |
Exemplo
@Before("execution(* com.example.service.*.*(..))")
public void logBefore() {
System.out.println("Method execution started");
}
Pointcut
Um Pointcut define onde um advice deve ser aplicado.
Exemplo:
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
Este pointcut seleciona todos os métodos dentro do pacote de serviço.
Target Object
O Target Object (Objeto Alvo) é o objeto cuja execução de método está sendo interceptada.
Exemplo:
@Service
public class PaymentService {
}
PaymentService é o objeto alvo.
Weaving
Weaving (Tecer) é o processo de conectar aspectos a objetos alvo para criar um objeto aconselhado (advised object).
Spring realiza o weaving em tempo de execução usando proxies.
Proxies AOP
O que é um Proxy?
Um proxy é um objeto criado pelo Spring que fica entre o cliente e o objeto alvo.
Em vez de chamar o alvo diretamente:
Cliente → PaymentService
A chamada passa por um proxy:
Cliente → Proxy → PaymentService
O proxy executa quaisquer aspectos configurados antes e/ou depois de chamar o método alvo.
Por que os Proxies são Necessários?
Spring AOP é baseado em proxy.
O proxy é responsável por:
- Interceptar chamadas de método
- Aplicar advice
- Invocar o método alvo
- Retornar resultados
Sem proxies, o Spring não seria capaz de injetar comportamento transversal dinamicamente.
Tipos de Proxies no Spring
JDK Dynamic Proxy
Usado quando a classe alvo implementa uma interface.
public interface PaymentService {
void processPayment();
}
@Service
public class PaymentServiceImpl implements PaymentService {
}
Spring cria um proxy baseado na interface.
CGLIB Proxy
Usado quando nenhuma interface existe.
@Service
public class PaymentService {
public void processPayment() {
}
}
Spring gera uma subclasse dinamicamente em tempo de execução.
Regras de Seleção de Proxy
| Cenário | Tipo de Proxy |
|---|---|
| Classe implementa interface | JDK Dynamic Proxy |
| Classe sem interface | CGLIB |
| force proxyTargetClass=true | CGLIB |
Exemplo:
@EnableAspectJAutoProxy(proxyTargetClass = true)
Limitação dos Proxies do Spring
Spring AOP só intercepta chamadas de método que passam pelo proxy.
Exemplo:
@Service
public class UserService {
public void methodA() {
methodB();
}
public void methodB() {
System.out.println("Method B");
}
}
Se um aspecto for aplicado a methodB(), ele não será acionado quando methodA() o chamar internamente porque a chamada não passa pelo proxy.
Isso é conhecido como o Problema de Auto-Invocação.
Expressões de Pointcut
O que é uma Expressão de Pointcut?
Uma Expressão de Pointcut é uma regra usada para selecionar quais métodos devem receber um advice.
Spring usa a sintaxe de expressão do AspectJ.
Sintaxe Básica
execution(modifiers-pattern?
return-type-pattern
declaring-type-pattern?
method-name-pattern
parameter-pattern
throws-pattern?)
Exemplo:
execution(* com.example.service.*.*(..))
Wildcards
Corresponder a Qualquer Tipo de Retorno
execution(* com.example.service.*.*(..))
* significa qualquer tipo de retorno.
Corresponder a Qualquer Nome de Método
execution(* com.example.service.*.*(..))
Corresponde a todos os métodos.
Corresponder a Quaisquer Parâmetros
execution(* com.example.service.*.*(..))
(..) significa zero ou mais parâmetros.
Expressões de Pointcut Comuns
Todos os Métodos em um Pacote
execution(* com.example.service.*.*(..))
Todos os Métodos em Subpacotes
execution(* com.example.service..*.*(..))
Os dois pontos (..) incluem subpacotes.
Método Específico
execution(* com.example.service.UserService.findUser(..))
Métodos Começando com “find”
execution(* com.example.service.*.find*(..))
Exemplos:
findUser()
findById()
findAll()
Métodos com Tipo de Retorno Específico
execution(List com.example.service.*.*(..))
Corresponde apenas a métodos que retornam List.
Métodos com Um Parâmetro String
execution(* *(String))
Métodos com Qualquer Número de Parâmetros
execution(* *(..))
Usando Pointcuts Nomeados
Em vez de repetir expressões:
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceLayer() {}
Reutilize-as:
@Before("serviceLayer()")
public void logBefore() {
}
Isso melhora a legibilidade e a manutenibilidade.
Exemplo Completo
@Aspect
@Component
public class LoggingAspect {
@Pointcut("execution(* com.example.service.*.*(..))")
public void serviceMethods() {}
@Before("serviceMethods()")
public void beforeMethod() {
System.out.println("Method started");
}
@AfterReturning("serviceMethods()")
public void afterMethod() {
System.out.println("Method finished");
}
}
Vantagens do Spring AOP
- Separa as preocupações transversais da lógica de negócio.
- Melhora a legibilidade do código.
- Reduz a duplicação de código.
- Manutenção mais fácil.
- Centraliza logging, segurança, auditoria e monitoramento.
- Integra-se perfeitamente com o Spring Framework.
Desvantagens do Spring AOP
- Pode tornar o fluxo da aplicação mais difícil de seguir.
- A depuração pode se tornar mais complexa.
- Limitação de auto-invocação devido à implementação baseada em proxy.
- A criação de proxy em tempo de execução introduz uma pequena sobrecarga.
- Suporta apenas join points de execução de método (ao contrário do AspectJ completo).
Resumo
Spring AOP é um recurso poderoso que permite aos desenvolvedores modularizar preocupações transversais como logging, segurança, monitoramento e gerenciamento de transações. Ele funciona através de proxies gerados em tempo de execução que interceptam chamadas de método e aplicam advice com base em expressões de pointcut. Compreender os conceitos centrais — Aspect, Advice, Join Point, Pointcut, Proxy e Weaving — é essencial para usar efetivamente o AOP em aplicações Spring.