Spring - Aspect Oriented Programming (AOP)

Aspect-Oriented Programming (AOP) allows you to separate cross-cutting concerns (logging, security, transaction management, performance monitoring) from your business logic. Spring AOP implements this using proxies and a rich annotation-based programming model.

Contents

Key AOP Concepts

Add spring-boot-starter-aop to your dependencies. Spring Boot enables @EnableAspectJAutoProxy automatically.

@Before advice: Logging example

A @Before advice runs before the matched method executes.

Java
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import java.util.Arrays;

@Aspect
@Component
public class LoggingAspect {

    // Matches all methods in any class under io.cscode.spring.service
    @Before("execution(* io.cscode.spring.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        System.out.println("Calling: " + joinPoint.getSignature().getName()
                + " args: " + Arrays.toString(joinPoint.getArgs()));
    }
}

@After, @AfterReturning, @AfterThrowing

@After runs after the method exits (regardless of outcome). @AfterReturning runs on successful return and can capture the return value. @AfterThrowing runs when an exception is thrown.

Java
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class AuditAspect {

    private static final String PC = "execution(* io.cscode.spring.service.*.*(..))";

    @AfterReturning(pointcut = PC, returning = "result")
    public void logReturn(Object result) {
        System.out.println("Returned: " + result);
    }

    @AfterThrowing(pointcut = PC, throwing = "ex")
    public void logException(Exception ex) {
        System.err.println("Exception: " + ex.getMessage());
    }

    @After(PC)
    public void logFinally() {
        System.out.println("Method completed (finally).");
    }
}

@Around advice: method timing example

@Around is the most powerful advice: it wraps the target method entirely. Use it for timing, caching, transactions, and retry logic.

Java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class TimingAspect {

    // Applied to any method annotated with @Timed
    @Around("@annotation(io.cscode.spring.annotation.Timed)")
    public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        try {
            return pjp.proceed();   // execute the actual method
        } finally {
            long elapsed = System.currentTimeMillis() - start;
            System.out.printf("%s executed in %d ms%n",
                    pjp.getSignature().toShortString(), elapsed);
        }
    }
}