Programação orientada a aspectos (AOP) em Java e C
1. Fundamentos da Programação Orientada a Aspectos
1.1. Conceitos centrais: aspectos, join points, pointcuts e advices
A Programação Orientada a Aspectos (AOP) é um paradigma que complementa a Programação Orientada a Objetos (OOP) ao permitir a modularização de preocupações que atravessam múltiplas camadas de um sistema. Os conceitos fundamentais incluem:
- Aspecto (Aspect): Unidade modular que encapsula um comportamento transversal. Em Java com AspectJ, um aspecto é declarado com a palavra-chave
aspect; em C#, utiliza-se uma classe com atributos personalizados. - Join Point: Ponto específico na execução do programa onde um aspecto pode ser aplicado (ex.: chamada de método, acesso a campo, lançamento de exceção).
- Pointcut: Expressão que seleciona um conjunto de join points. Exemplo:
execution(* com.exemplo..*.*(..))captura todos os métodos em um pacote. - Advice: Código executado em um join point selecionado. Tipos comuns:
@Before,@After,@Around.
1.2. Separação de preocupações transversais (cross-cutting concerns)
Preocupações transversais são responsabilidades que não podem ser isoladas em um único módulo OOP tradicional. Exemplos clássicos:
- Logging e auditoria
- Segurança e controle de acesso
- Gerenciamento de transações
- Cache e tratamento de exceções
Sem AOP, essas preocupações geram código espalhado (scattered) e duplicado (tangled) em diversos métodos. AOP permite extraí-las para aspectos reutilizáveis.
1.3. Diferenças entre AOP e paradigmas tradicionais (OOP, funcional)
Enquanto OOP organiza o código em torno de classes e objetos, e a programação funcional em torno de funções puras, a AOP foca em comportamentos que cruzam fronteiras modulares. AOP não substitui OOP, mas atua como camada adicional para gerenciar cross-cutting concerns de forma declarativa.
2. Implementação de AOP em Java com AspectJ
2.1. Sintaxe básica do AspectJ
AspectJ é a implementação mais madura de AOP para Java, oferecendo weaving em tempo de compilação (compile-time weaving) e em tempo de carga (load-time weaving). Um aspecto básico:
public aspect LoggingAspect {
pointcut metodoPublico() : execution(public * *(..));
before() : metodoPublico() {
System.out.println("[LOG] Executando: " + thisJoinPoint.getSignature());
}
}
2.2. Tipos de advices: @Before, @After, @Around e @AfterThrowing
Com suporte a anotações (AspectJ 5+):
import org.aspectj.lang.annotation.*;
@Aspect
public class TransacaoAspect {
@Around("execution(* com.exemplo.servico.*.salvar(..))")
public Object gerenciarTransacao(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("[TRANSACAO] Iniciando transação");
try {
Object resultado = pjp.proceed();
System.out.println("[TRANSACAO] Commit");
return resultado;
} catch (Exception e) {
System.out.println("[TRANSACAO] Rollback: " + e.getMessage());
throw e;
}
}
@AfterThrowing(pointcut = "execution(* com.exemplo..*.*(..))", throwing = "ex")
public void logErro(Exception ex) {
System.err.println("[ERRO] Exceção capturada: " + ex.getMessage());
}
}
2.3. Exemplos práticos: logging, segurança e transações com AspectJ
// Aspecto de segurança
@Aspect
public class SegurancaAspect {
@Before("execution(* com.exemplo.recursos.*.deletar(..)) && args(usuarioId, ..)")
public void verificarPermissao(long usuarioId) {
if (!UsuarioLogado.temPermissao(usuarioId)) {
throw new SecurityException("Acesso negado");
}
}
}
3. AOP em Java com Spring AOP
3.1. Configuração baseada em anotações
Spring AOP utiliza proxies dinâmicos (JDK ou CGLIB) e é mais limitado que AspectJ, mas integra-se nativamente ao ecossistema Spring:
import org.springframework.stereotype.Component;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {}
@Component
@Aspect
public class CacheAspect {
private Map<String, Object> cache = new HashMap<>();
@Around("@annotation(com.exemplo.anotacoes.Cacheavel)")
public Object cachear(ProceedingJoinPoint pjp) throws Throwable {
String chave = pjp.getSignature().toShortString() + Arrays.toString(pjp.getArgs());
if (cache.containsKey(chave)) {
return cache.get(chave);
}
Object resultado = pjp.proceed();
cache.put(chave, resultado);
return resultado;
}
}
3.2. Proxies dinâmicos vs. AspectJ nativo
| Característica | Spring AOP (Proxy) | AspectJ |
|---|---|---|
| Weaving | Tempo de execução | Compilação/carga |
| Join points | Apenas métodos públicos | Métodos, campos, construtores |
| Performance | Leve overhead | Maior overhead inicial |
| Complexidade | Baixa | Alta |
3.3. Integração com o ecossistema Spring
Spring AOP é amplamente usado para @Transactional, @Secured e cache declarativo, dispensando configuração manual de aspectos.
4. AOP em C# com Aspect-Oriented Programming Frameworks
4.1. Abordagens nativas: atributos personalizados e interceptadores
C# não possui suporte nativo a AOP, mas permite criar interceptadores via Castle.Core DynamicProxy:
using Castle.DynamicProxy;
public class LogInterceptor : IInterceptor {
public void Intercept(IInvocation invocation) {
Console.WriteLine($"[LOG] Chamando {invocation.Method.Name}");
invocation.Proceed();
Console.WriteLine($"[LOG] Finalizado {invocation.Method.Name}");
}
}
// Uso
var proxy = new ProxyGenerator().CreateClassProxy<MeuServico>(new LogInterceptor());
4.2. Frameworks populares: PostSharp, Fody
PostSharp (comercial) oferece AOP via atributos em tempo de compilação:
[Serializable]
public class TratarExcecaoAttribute : OnExceptionAspect {
public override void OnException(MethodExecutionArgs args) {
Console.WriteLine($"Exceção em {args.Method.Name}: {args.Exception.Message}");
args.FlowBehavior = FlowBehavior.Return; // Suprime exceção
}
}
public class Servico {
[TratarExcecao]
public void Operacao() {
throw new InvalidOperationException("Erro simulado");
}
}
Fody (gratuito, open-source) permite weaving em tempo de compilação com add-ins como PropertyChanged.Fody e MethodBoundaryAspect.Fody.
4.3. Exemplos práticos: cache, validação e tratamento de exceções em C
// Cache com PostSharp
[Serializable]
public class CacheAttribute : MethodInterceptionAspect {
private static Dictionary<string, object> _cache = new Dictionary<string, object>();
public override void OnInvoke(MethodInterceptionArgs args) {
string chave = args.Method.Name + string.Join(",", args.Arguments);
if (_cache.ContainsKey(chave)) {
args.ReturnValue = _cache[chave];
return;
}
args.Proceed();
_cache[chave] = args.ReturnValue;
}
}
5. Comparação entre AOP em Java e C
5.1. Diferenças de sintaxe e paradigma
Java utiliza anotações (@Aspect, @Before) e sintaxe dedicada (AspectJ). C# usa atributos ([Serializable], [OnMethodBoundaryAspect]) e frameworks externos.
5.2. Suporte a compilação vs. tempo de execução
- Java: AspectJ oferece weaving estático (compilação) e dinâmico (carga). Spring AOP é exclusivamente em tempo de execução via proxies.
- C#: PostSharp e Fody fazem weaving em tempo de compilação. Castle.Core opera em tempo de execução.
5.3. Performance, maturidade de frameworks e adoção em projetos reais
AspectJ e Spring AOP são amplamente adotados no ecossistema Java Enterprise. No mundo .NET, PostSharp é o padrão industrial, com Fody ganhando tração em projetos open-source. Para aplicações críticas de performance, o weaving em tempo de compilação (AspectJ, PostSharp) é preferível.
6. Padrões de Projeto com AOP
6.1. Decorator pattern e sua relação com AOP
O padrão Decorator adiciona comportamento a objetos dinamicamente. AOP automatiza esse padrão, permitindo que aspectos atuem como decoradores declarativos.
6.2. Template Method e interceptação de métodos
AOP pode substituir o Template Method quando a variação de comportamento está em preocupações transversais, como logging ou validação.
6.3. Combinação com injeção de dependência e inversão de controle
Spring AOP e Castle.Core integram-se naturalmente com IoC containers, permitindo que aspectos sejam injetados como dependências.
7. Boas Práticas e Armadilhas Comuns
7.1. Quando usar AOP: cenários ideais e limitações
Use AOP para preocupações verdadeiramente transversais (logging, segurança, transações). Evite para lógica de negócio central, que deve permanecer em OOP puro.
7.2. Evitando efeitos colaterais: previsibilidade e depuração
Aspectos podem tornar o fluxo do programa não óbvio. Documente cada aspecto e limite o escopo dos pointcuts para evitar surpresas.
7.3. Manutenção de aspectos: modularidade e documentação
Mantenha aspectos em pacotes separados, nomeie pointcuts expressivamente e inclua testes unitários para cada advice.
Referências
- AspectJ Project — Documentação oficial do AspectJ, incluindo referência de linguagem e guias de weaving.
- Spring AOP Reference — Guia completo do Spring AOP com exemplos de configuração baseada em anotações e XML.
- PostSharp Documentation — Documentação oficial do PostSharp com tutoriais sobre atributos de aspecto e integração com .NET.
- Castle.Core DynamicProxy — Repositório oficial do Castle.Core, incluindo exemplos de interceptação dinâmica em C#.
- Fody MethodBoundaryAspect — Add-in do Fody para criação de aspectos baseados em limites de método, com exemplos práticos de cache e logging.
- AOP em Java: Conceitos e Práticas — Tutorial da Baeldung sobre conceitos de AOP e implementação com Spring e AspectJ.
- AOP em C# com Atributos — Documentação da Microsoft sobre atributos personalizados, base para implementação de AOP em .NET.