2.6 Spring AOP & Proxies
← Back to Chapter 2: Spring Beans & Dependency Injection
Aspect-Oriented Programming (AOP) modularizes cross-cutting concerns (e.g., logging, transaction management, rate limiting, security checks) without cluttering business logic methods.
1. Core AOP Terminology
┌─────────────────────────────────────────────────────────────┐
│ Aspect: A modular unit of cross-cutting concern (e.g. Audit)│
├─────────────────────────────────────────────────────────────┤
│ Join Point: A point during program execution (method call) │
├─────────────────────────────────────────────────────────────┤
│ Pointcut: A predicate matching target Join Points │
│ execution(* com.service.*.*(..)) │
├─────────────────────────────────────────────────────────────┤
│ Advice: Action taken at a Join Point (@Before, @Around...) │
├─────────────────────────────────────────────────────────────┤
│ Target Object: The business object being advised │
├─────────────────────────────────────────────────────────────┤
│ Weaving: Linking aspects with target objects to create proxy│
└─────────────────────────────────────────────────────────────┘
2. Advice Types & Code Example
@Aspect
@Component
public class PerformanceTrackerAspect {
private static final Logger log = LoggerFactory.getLogger(PerformanceTrackerAspect.class);
// Matches any public method execution inside service package
@Pointcut("execution(public * com.codewithsarvy.service..*.*(..))")
public void serviceLayerMethods() {}
@Around("serviceLayerMethods()")
public Object measureExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try {
// Proceed with actual business method invocation
return pjp.proceed();
} finally {
long duration = System.currentTimeMillis() - start;
log.info("Method [{}.{}] executed in {} ms",
pjp.getTarget().getClass().getSimpleName(),
pjp.getSignature().getName(),
duration);
}
}
}
Advice Kinds:
@Before: Executes before the target method is called.@AfterReturning: Executes only if the target method completes normally without exception.@AfterThrowing: Executes only if an exception is thrown by the method.@After(Finally): Executes regardless of whether the method threw or returned.@Around: Most powerful advice; wraps target method, controls execution flow, and can modify arguments or return values.
3. CGLIB vs JDK Dynamic Proxies
Spring creates runtime proxies to intercept method calls for AOP and transactions:
JDK Dynamic Proxy: [ Client ] ──> [ Interface Proxy ] ──> [ Target Class ]
(Requires Interface)
CGLIB Proxy: [ Client ] ──> [ Subclass Proxy (extends Target) ]
(Bytecode Generation)
| Proxy Type | Mechanism | Requirements / Constraints |
|---|---|---|
| JDK Dynamic Proxy | Uses Java reflection java.lang.reflect.Proxy | Target class must implement at least one interface. |
| CGLIB Proxy | Generates dynamic subclasses at runtime | Target class & advised methods cannot be final. |
Spring Boot 2.x/3.x Default: Spring Boot defaults to CGLIB proxies for all beans (
spring.aop.proxy-target-class=true), even if the bean implements an interface. This avoids frustrating class-cast exceptions when injecting concrete types.
The Self-Invocation Gotcha:
Because Spring AOP works via external proxy interception, calling a transactional method from within the same class (this.myTransactionalMethod()) bypasses the proxy and transactional advice is NOT executed!