Skip to main content

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 TypeMechanismRequirements / Constraints
JDK Dynamic ProxyUses Java reflection java.lang.reflect.ProxyTarget class must implement at least one interface.
CGLIB ProxyGenerates dynamic subclasses at runtimeTarget 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!