Internal Mechanics of AspectJ Advice Types and Execution Order in Spring AOP
Core Interceptor Implementations for AspectJ Advice
Spring AOP translates AspectJ annotations into a chain of MethodInterceptor instances. Each advice type corresponds to a specific interceptor that dictates when the cross-cutting logic executes relative to the target method.
Before Advice Execution
The before advice interceptor guarantees that the advisory logic runs strictly prior to the target method invocation. It does not wrap the execution in a try-catch or try-finally block, meaning it cannot intercept exceptions or alter the return value.
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
private final MethodBeforeAdvice delegate;
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
this.delegate = advice;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// Trigger the cross-cutting logic first
this.delegate.before(invocation.getMethod(), invocation.getArguments(), invocation.getThis());
// Continue down the interceptor chain to the target method
return invocation.proceed();
}
}
After (Finally) Advice Execution
This interceptor ensures the advice runs regardless of whether the target method completes successfully or throws an exception. It relies on a finally block to guarantee execution.
public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
// Proceed with the chain
return invocation.proceed();
} finally {
// Execute the finally-bound advice logic
executeAdviceMethod(resolveJoinPointMatch(), null, null);
}
}
}
AfterReturning Advice Execution
The returning advice only triggers when the target method completes without throwing an exception. It captures the result, passes it to the advice callback, and then returns it to the caller.
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
private final AfterReturningAdvice callback;
public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
this.callback = advice;
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// Execute the target method and capture the result
Object result = invocation.proceed();
// Pass the result to the advice handler
this.callback.afterReturning(result, invocation.getMethod(), invocation.getArguments(), invocation.getThis());
return result;
}
}
AfterThrowing Advice Execution
This interceptor is designed specifically for error handling. It wraps the invocation in a try-catch block, evaluates the caught exception against the pointcut's throwing condition, executes the error logic, and re-throws the original exception.
public class AspectJAfterThrowingAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice, Serializable {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
try {
return invocation.proceed();
} catch (Throwable error) {
// Verify if the exception matches the configured throwing condition
if (matchesThrowingCondition(error)) {
executeAdviceMethod(resolveJoinPointMatch(), null, error);
}
// Propagate the original exception up the stack
throw error;
}
}
}
Note that AfterReturning and AfterThrowing are mutually exclusive. A method invocation will either yield a return value or propagate an exception, never both.
Around Advice Execution
The around advice provides the most comprehensive control. It constructs a ProceedingJoinPoint and passes it directly to the advice method. This allows the advice to decide whether to proceed, modify arguments, alter the return value, or suppress exceptions entirely.
public class AspectJAroundAdvice extends AbstractAspectJAdvice implements MethodInterceptor, Serializable {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
if (!(invocation instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("Invalid invocation type for around advice");
}
ProxyMethodInvocation proxyInvocation = (ProxyMethodInvocation) invocation;
// Lazily initialize the join point wrapper
ProceedingJoinPoint joinPoint = buildProceedingJoinPoint(proxyInvocation);
JoinPointMatch pointMatch = extractJoinPointMatch(proxyInvocation);
// Delegate to the user-defined around method
return executeAdviceMethod(joinPoint, pointMatch, null, null);
}
}
Execution Order Variations Across Spring Framework Versions
The sequencing of advice execution within the interceptor chain has evolved between major framework releases. This difference is particularly noticeable when comparing Spring Framework 4.x (used in Spring Boot 1.x) and Spring Framework 5.x (used in Spring Boot 2.x).
Verification Setup
To observe the ordering, a simple service and an aspect containing all five advice types can be configured:
@Service
public class TargetBusinessService {
public void processTask() {
System.out.println("[TARGET] Executing business logic");
}
}
@Aspect
@Component
public class OrderTrackingAspect {
@Pointcut("execution(* com.example.service.TargetBusinessService.*(..))")
public void serviceMethods() {}
@Before("serviceMethods()")
public void handleBefore() {
System.out.println("[BEFORE] Pre-execution logic");
}
@After("serviceMethods()")
public void handleAfter() {
System.out.println("[AFTER] Post-execution logic");
}
@AfterReturning("serviceMethods()")
public void handleReturn() {
System.out.println("[RETURN] Successful completion logic");
}
@AfterThrowing("serviceMethods()")
public void handleError() {
System.out.println("[THROW] Exception handling logic");
}
@Around("serviceMethods()")
public Object handleAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("[AROUND] Pre-proceed logic");
Object outcome = pjp.proceed();
System.out.println("[AROUND] Post-proceed logic");
return outcome;
}
}
Spring Framework 5.x Ordering
Executing the target method in a Spring Boot 2.x environment yields the following console output:
[AROUND] Pre-proceed logic
[BEFORE] Pre-execution logic
[TARGET] Executing business logic
[RETURN] Successful completion logic
[AFTER] Post-execution logic
[AROUND] Post-proceed logic
In version 5.x, the around advice completely encapsulates the entire interceptor chain. The execution flow strictly follows a nested structure where the around advice's post-proceed logic executes last, after all other post-invocation advices have completed.
Spring Framework 4.x Ordering
Downgrading the environment to Spring Boot 1.x (Spring Framework 4.x) alters the sequence:
[AROUND] Pre-proceed logic
[BEFORE] Pre-execution logic
[TARGET] Executing business logic
[AROUND] Post-proceed logic
[AFTER] Post-execution logic
[RETURN] Successful completion logic
The legacy 4.x implementation positions the around advice differently within the chain. Its post-proceed block executes immediately after the target method, before the @After and @AfterReturning interceptors are triggered. Consequently, the around advice in 4.x only wraps the before advice and the target method, rather than the entire advice chain. This architectural shift in 5.x provides a more intuitive and consistent neesting model for cross-cutting concerns.