skip to content

AspectJ distinguishes execution() from call() and offers get()/set()/initialization(). Which of these does Spring AOP actually implement, and why?

level: seniorimportance: should knowfreq 45%

answer

  1. Spring = execution only
  2. call/get/set/initialization → AspectJ weaving
  3. Unsupported → UnsupportedPointcutPrimitiveException
  4. within/this/target/args/@annotation OK
  5. this=proxy, target=bean

basics

~10 s

Spring AOP implements only method execution. It does not support call, get/set (fields), initialization (constructors), staticinitialization, or handler, because a runtime proxy can only intercept a method being executed on the wrapped bean.

solid answer

~40 s

Spring AOP reuses AspectJ's pointcut *syntax* but is a runtime proxy, so it maps only to AspectJ's method **`execution`** join point. `call()` matches the caller's call *site* — a proxy can't see who calls it, only that its own method runs — so Spring omits it (in practice `execution` is what you want under Spring anyway). `get()`/`set()` (field access), `initialization()`/`preinitialization()` (constructors), `staticinitialization`, and `handler` (exception handlers) all need bytecode-level weaving and have no proxy hook, so Spring rejects them — using them yields an `UnsupportedPointcutPrimitiveException` at startup. Supporting designators that *do* work in Spring (as matchers narrowing method execution) include `within`, `this`, `target`, `args`, `@annotation`, `@within`, `@target`, `@args`, and `bean`. For any unsupported join point you need full AspectJ (CTW or LTW).

code

java · 19 lines
java
@Aspect
@Component
public class PcdDemoAspect {

    // SUPPORTED: execution + refining designators
    @Around("execution(* com.example..*Service.*(..)) && @annotation(org.springframework.transaction.annotation.Transactional)")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        return pjp.proceed();
    }

    // SUPPORTED: bean() and target() narrow method-execution join points
    @Before("bean(orderService) && target(com.example.OrderService)")
    public void beforeOrderBean() { }

    // UNSUPPORTED in Spring AOP -> UnsupportedPointcutPrimitiveException at startup:
    // @Before("call(* com.example..*.*(..))")           // call site
    // @After("get(* com.example..*)")                    // field read
    // @After("initialization(com.example..*.new(..))")   // constructor
}

go deeper

for a junior

Just knowing 'Spring = method execution only' is enough at this level.

for a middle

Should know that field/constructor pointcuts aren't supported.

for a senior

Enumerate supported vs unsupported PCDs and know the startup exception for unsupported ones.

for a principal

Reason about this()/target() proxy semantics and articulate why the runtime-proxy model precludes weaving-only join points.

## AspectJ's pointcut designators (PCDs) AspectJ defines many **kinds** of join points, each selected by a pointcut designator: - **`execution(...)`** — the **execution** of a method/constructor body. Matched *inside* the callee. - **`call(...)`** — the **call site** of a method/constructor. Matched *inside the caller*, before dispatch. - **`get(...)` / `set(...)`** — reading / writing a **field**. - **`initialization(...)` / `preinitialization(...)`** — **constructor** execution join points. - **`staticinitialization(...)`** — a class's **static initializer** running. - **`handler(...)`** — an **exception handler** (catch block) executing. - **`adviceexecution()`** — execution of advice itself. ## What Spring AOP implements Spring AOP is **proxy-based**, purely a runtime construct. The proxy can observe exactly one thing: *its own method is being executed on the target bean*. Therefore Spring maps only to AspectJ's **`execution`** join point. Everything requiring visibility into other program points needs **bytecode weaving**, which Spring's proxies don't do: - **`call`** — would require instrumenting the *caller's* code. A proxy can't see the call site. (Note: under Spring, `execution` already covers the practical need, since interception is on the callee anyway.) - **`get`/`set`** — no field-access hook in a proxy. - **`initialization`/`preinitialization`** — the proxy is created *after* the target is constructed, so it can't wrap construction. - **`staticinitialization`, `handler`** — no proxy hook at all. If you write a Spring `@Aspect` pointcut using an unsupported primitive (e.g. `@Before("set(* *..*)")`), Spring's pointcut parser throws `org.springframework.aop.framework.AopConfigException` wrapping an **`UnsupportedPointcutPrimitiveException`** at context startup. ## Supported designators that *refine* method execution These work in Spring AOP because they merely **narrow** which method-execution join points match — they don't introduce new join-point kinds: - **`within(pkg..*)`** — join points within given types. - **`this(Type)`** — proxy is an instance of Type (matches the *proxy*). - **`target(Type)`** — target object is an instance of Type. - **`args(...)`** — argument types/values. - **`@annotation(Ann)`** — method carries annotation `Ann`. - **`@within` / `@target`** — the declaring/target *class* carries an annotation. - **`@args`** — argument runtime types carry an annotation. - **`bean(name)`** — Spring-specific: match by bean name/pattern. ## Subtlety: `this` vs `target` under proxies Because Spring interposes a proxy, `this()` refers to the **proxy object** and `target()` to the **underlying bean**. With a JDK proxy the proxy is *not* an instance of the concrete class (only its interfaces), which affects `this()` matching — a real senior-level nuance. ## Why this matters (the granularity gap) Proxy AOP's method-execution-only model is deliberately narrow: it keeps AOP a lightweight, no-build-step runtime feature that cleanly covers the dominant needs (transactions, security, caching, logging on inter-bean method calls). When you need finer granularity — trace every field mutation for auditing, intercept domain-object constructors, weave into non-Spring classes — you graduate to **full AspectJ**: compile-time weaving (`ajc`) or load-time weaving (`spring-instrument` agent + `@EnableLoadTimeWeaving`). AspectJ rewrites bytecode, unlocking every PCD above. ## When to use which - Default to Spring AOP; it's enough for annotation-driven concerns on bean method boundaries. - Move to AspectJ only for the non-method join points or to reach objects Spring doesn't manage — accepting the extra build/agent complexity.

  • Under Spring AOP, what's the practical difference between execution() and call()?
    execution() matches inside the callee (the method being run); call() matches at the caller's call site. Spring only supports execution() because the proxy intercepts the callee. For Spring's proxy model, execution() is the natural and only choice; call() would need the caller's bytecode woven.
  • What happens at startup if you use set() in a Spring @Aspect pointcut?
    Spring's pointcut parser can't handle it and throws an AopConfigException wrapping UnsupportedPointcutPrimitiveException, failing the application context startup.

saying these in an interview costs you the question

  • Claiming Spring supports call() as an alias of execution()
  • Saying get()/set() work if you enable @EnableAspectJAutoProxy
  • Confusing this() and target() (thinking this() is the raw bean)

context