skip to content

How does Spring's classic advice API relate to @AspectJ advice, and how are the simpler advice types executed inside the proxy?

level: principalimportance: nice to knowfreq 25%

answer

  1. One runtime: chain of MethodInterceptors
  2. AdvisorAdapterRegistry wraps before/afterReturning/throws into interceptors
  3. @AspectJ = advisors via AnnotationAwareAspectJAutoProxyCreator
  4. @Around=interceptor, @Before=MethodBeforeAdvice, @AfterThrowing=ThrowsAdvice
  5. shared: JDK/CGLIB, Ordered, self-invocation

basics

~10 s

Every advice type ultimately runs as a MethodInterceptor in the proxy's chain. Simpler advices (before/after-returning/throws) are wrapped by adapters into interceptors. @AspectJ advice is a higher-level, annotation-driven layer built on the same Advisor/interceptor plumbing.

solid answer

~40 s

Spring AOP has one runtime model: a proxy holds an ordered chain of org.aopalliance.intercept.MethodInterceptor instances plus the target, and each call flows through them via MethodInvocation.proceed(). The classic advice interfaces are conveniences on top: MethodBeforeAdvice, AfterReturningAdvice, and ThrowsAdvice are not run directly — the DefaultAdvisorAdapterRegistry uses AdvisorAdapters (MethodBeforeAdviceAdapter, AfterReturningAdviceAdapter, ThrowsAdviceAdapter) to wrap each into an equivalent MethodInterceptor when the chain is assembled. @AspectJ (@Before/@Around/@AfterReturning/@AfterThrowing/@After) is a still-higher layer: annotated methods are turned into Advisors (InstantiationModelAwarePointcutAdvisor) with AspectJ pointcut expressions, and their advice is likewise adapted to interceptors. So @AspectJ and the classic API are two authoring styles over identical Advisor + interceptor-chain internals; ordering, proxy creation (JDK vs CGLIB), and self-invocation limits are shared by both.

code

java · 18 lines
java
// Conceptually, the adapter turns a MethodBeforeAdvice into an interceptor
// like MethodBeforeAdviceInterceptor does internally:
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.MethodBeforeAdvice;

public class MethodBeforeAdviceInterceptorLike implements MethodInterceptor {
    private final MethodBeforeAdvice advice;
    public MethodBeforeAdviceInterceptorLike(MethodBeforeAdvice advice) { this.advice = advice; }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); // run the 'before'
        return mi.proceed(); // then the rest of the chain / target
    }
}
// Spring's real classes: MethodBeforeAdviceInterceptor, AfterReturningAdviceInterceptor,
// ThrowsAdviceInterceptor — supplied by DefaultAdvisorAdapterRegistry.

go deeper

for a junior

Just know @AspectJ is the modern style and the classic API is the older, lower-level one.

for a middle

State that all advice ends up as interceptors and map @Around/@Before to interceptor/before advice.

for a senior

Describe the AdvisorAdapter conversion and that @AspectJ produces Advisors via an auto-proxy creator.

for a principal

Discuss AdvisorAdapterRegistry as an extension seam, proxy-based (not woven) semantics, chain ordering, and when to drop to the classic API.

### One runtime, several authoring styles A crucial mental model: **at runtime Spring AOP always executes a chain of `MethodInterceptor`s**. Everything else — classic before/after advice, `@AspectJ` annotations, `<aop:config>` XML, `@Transactional` — is a **higher-level way to produce `Advisor`s** whose advice is ultimately turned into interceptors in that chain. ### Adapting classic advice into interceptors The non-interceptor advice types can't be called directly by the invocation machinery, so Spring adapts them: - `org.springframework.aop.framework.adapter.AdvisorAdapterRegistry` (default impl `DefaultAdvisorAdapterRegistry`) holds a set of `AdvisorAdapter`s: - `MethodBeforeAdviceAdapter` → wraps a `MethodBeforeAdvice` in a `MethodBeforeAdviceInterceptor` (calls `before(...)`, then `proceed()`). - `AfterReturningAdviceAdapter` → `AfterReturningAdviceInterceptor` (calls `proceed()`, then `afterReturning(...)`). - `ThrowsAdviceAdapter` → `ThrowsAdviceInterceptor` (wraps `proceed()` in try/catch and reflectively dispatches to the matching `afterThrowing` method). - When the proxy is built, each `Advisor`'s advice is passed through the registry's `getInterceptors(advisor)` to yield the actual interceptors for the chain. This is an **extension point**: you can register custom `AdvisorAdapter`s to support your own advice interfaces. ### @AspectJ as a layer above `@AspectJ`-style aspects (classes annotated `@Aspect` with `@Before`/`@Around`/`@AfterReturning`/`@AfterThrowing`/`@After` methods) are **not** woven by the AspectJ compiler in Spring — Spring only borrows the **annotations and pointcut expression language**. At context startup, `AnnotationAwareAspectJAutoProxyCreator` (enabled by `@EnableAspectJAutoProxy` or `<aop:aspectj-autoproxy/>`): 1. Finds `@Aspect` beans. 2. Converts each advice method into an `Advisor` (`InstantiationModelAwarePointcutAdvisor`) whose pointcut is an `AspectJExpressionPointcut` and whose advice is an AspectJ advice object (e.g. `AspectJAroundAdvice`, `AspectJMethodBeforeAdvice`) — these implement `MethodInterceptor` or are adapted like the classic ones. 3. Feeds those advisors into the same proxy/chain mechanism. So `@Around` ↔ `MethodInterceptor`, `@Before` ↔ `MethodBeforeAdvice`, `@AfterReturning` ↔ `AfterReturningAdvice`, `@AfterThrowing` ↔ `ThrowsAdvice`, and `@After` (finally) has no classic single-interface equivalent (it maps to `AspectJAfterAdvice`, an interceptor with a `finally`). ### Shared mechanics (true for both styles) - **Proxy type**: JDK dynamic proxy if the target implements interfaces, else CGLIB subclass (or forced via `proxyTargetClass=true`). - **Ordering**: advisors/aspects implement `Ordered` or use `@Order`; the chain is sorted before execution. - **Self-invocation**: only calls through the proxy are advised; internal `this.method()` calls bypass advice in both styles. - **Singleton advice**: interceptors/advices are shared and must be thread-safe. ### Why this matters at a principal level - Explains why mixing classic advisors and `@AspectJ` aspects on the same bean works — they all collapse to one ordered interceptor chain. - Clarifies performance/debugging: a stack trace through advised calls shows `ReflectiveMethodInvocation.proceed` and the various `*Interceptor`s. - The `AdvisorAdapterRegistry` is the seam for integrating bespoke advice types or third-party AOP-Alliance interceptors. ### When to reach for the classic API today Prefer `@AspectJ` for readability in application code. Drop to the classic Advisor/interceptor API for: programmatic proxy creation (`ProxyFactory`), library/framework code that must not depend on annotation scanning, dynamic/generated pointcuts, or wrapping existing AOP-Alliance interceptors.

  • Which component converts a MethodBeforeAdvice or AfterReturningAdvice into a MethodInterceptor for the chain?
    The AdvisorAdapterRegistry (default DefaultAdvisorAdapterRegistry) with its AdvisorAdapters — MethodBeforeAdviceAdapter, AfterReturningAdviceAdapter, ThrowsAdviceAdapter — which produce the corresponding *Interceptor wrappers when the proxy chain is assembled.
  • Does Spring's @AspectJ support use the AspectJ compiler/weaver?
    No. Spring AOP is proxy-based; it reuses AspectJ's annotations and pointcut expression parser but weaves at runtime via proxies. Full compile/load-time weaving requires actual AspectJ (e.g. spring-aspects with LTW), not plain Spring AOP.
  • Can classic Advisors and @AspectJ aspects coexist on the same bean?
    Yes. Both become Advisors and are merged into one ordered interceptor chain per proxied bean; relative order is controlled by Ordered/@Order.

saying these in an interview costs you the question

  • Thinking before/after-returning advices run outside the interceptor chain via a special mechanism
  • Believing Spring @AspectJ requires the AspectJ compiler or load-time weaving
  • Assuming @AspectJ and classic advisors use different, incompatible runtimes
  • Claiming @After (finally) has a dedicated classic single-method advice interface

context