Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Internal Mechanics of AspectJ Advice Types and Execution Order in Spring AOP

Tech 1

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.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.