Skip to main content

Spring AOP (Aspect Oriented Programming)

AOP is one of the most powerful (and misunderstood) parts of Spring. It allows you to separate Cross-Cutting Concerns (logging, security, transactions) from your Business Logic.

1. The Concepts

  • Aspect: A module that encapsulates a concern (e.g., LoggingAspect).
  • Join Point: A point in execution (e.g., “Method execution of saveUser”).
  • Advice: Action taken at a join point (e.g., “Log ‘Enter method’”).
  • Pointcut: A predicate that matches join points (e.g., “All methods in com.service package”).
  • Weaving: Linking aspects with other objects to create an advised object (Proxy).

2. Advice Types

AdviceDescriptionUse Case
@BeforeRuns before the method.Auth checks, Logging arguments.
@AfterReturningRuns after successful execution.Audit logging “Success”.
@AfterThrowingRuns if exception is thrown.Error reporting.
@AfterRuns finally (finally block).Cleanup.
@AroundMost Powerful. Wraps the method. Can change args, return value, or stop execution.Transaction Mgmt, Performance Monitoring.

3. Implementation Example: Performance Monitoring

@Aspect
@Component
@Slf4j
public class PerformanceAspect {

    // Pointcut: All methods in 'service' package
    @Pointcut("execution(* com.devweekends.demo.service.*.*(..))")
    public void serviceMethods() {}

    @Around("serviceMethods()")
    public Object measureTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        
        // 1. Proceed with the actual method
        Object result = joinPoint.proceed(); 
        
        long end = System.currentTimeMillis();
        log.info("{} took {} ms", joinPoint.getSignature().getName(), (end - start));
        
        // 2. Return the result (or modify it!)
        return result;
    }
}

4. How it Works: The Proxy Pattern

Spring AOP uses Dynamic Proxies.

JDK vs CGLIB

  • JDK Dynamic Proxy: Used if the target implements an Interface. Only creates proxies for interfaces.
  • CGLIB (Code Generation Library): Used if the target is a Class (no interface). Spring generates a subclass of your bean at runtime.
Interview Tip: Since Spring Boot 2.0, CGLIB is the default (even for interfaces) to ensure consistency (prevents ClassCastException if you autowire the impl class).

5. Pitfall: Self-Invocation

Problem: Using @Transactional or @Async on a method calls from within the same class doesn’t work.
public class OrderService {
    public void createOrder() {
        // ...
        sendEmail(); // THIS WILL NOT BE ASYNC!
    }

    @Async
    public void sendEmail() { ... }
}
Reason: createOrder() calls this.sendEmail(). this refers to the Target Object, not the Proxy. The proxy is bypassed. Solution: Self-inject the bean or move the method to another service.