skip to content

A requirement needs you to intercept field writes and constructor calls across your codebase. How does Spring AOP's supported-designator subset constrain you, and what's the correct architectural move?

level: principalimportance: should knowfreq 22%

answer

  1. Method-only ceiling is architectural, not configurable
  2. Escalate to full AspectJ: LTW or CTW
  3. LTW = @EnableLoadTimeWeaving + javaagent + aop.xml
  4. CTW = aspectj-maven-plugin / ajc at build
  5. Trade-offs: agent ops, blast radius, debuggability

basics

~20 s

Spring AOP can't do it — it only advises method executions, so get/set (fields) and constructor join points aren't available. The move is to adopt full AspectJ via load-time or compile-time weaving, which supports the entire join-point model.

solid answer

~40 s

Spring AOP's proxy model exposes only method-execution join points, so `get`, `set`, `initialization`, and `staticinitialization` designators are unsupported and throw at startup. You cannot bolt them on with configuration. The correct move is to graduate to **full AspectJ weaving**: either **load-time weaving (LTW)** via `@EnableLoadTimeWeaving` plus a `-javaagent:aspectjweaver.jar` and a `META-INF/aop.xml`, or **compile-time/post-compile weaving** via the `aspectj-maven-plugin`. Both weave advice directly into bytecode, unlocking field access, constructor, call-site, and control-flow join points, and also eliminating the self-invocation blind spot. The trade-offs: added build/agent complexity, wider matching surface (weave non-Spring classes too), and performance/observability considerations. Spring lets `@Aspect` classes work under either engine, so migration is mostly infrastructure, not aspect rewrites — though your pointcuts can now legitimately use the previously-forbidden designators.

code

java · 19 lines
java
// Load-time weaving: unlocks field/constructor join points
@Configuration
@EnableLoadTimeWeaving  // needs -javaagent:aspectjweaver.jar at runtime
public class WeavingConfig { }

// META-INF/aop.xml declares what to weave:
// <aspectj>
//   <aspects><aspect name="com.acme.FieldAuditAspect"/></aspects>
//   <weaver options="-Xlint:ignore">
//     <include within="com.acme..*"/>
//   </weaver>
// </aspectj>

@Aspect
public class FieldAuditAspect {
    // Legal under AspectJ weaving, IMPOSSIBLE in Spring AOP:
    @Before("set(* com.acme.Account.balance)")
    public void onBalanceWrite() { /* audit */ }
}

go deeper

for a junior

Know Spring AOP can't touch fields/constructors and full AspectJ is the alternative.

for a middle

Name LTW/CTW as the escalation and roughly how each is enabled.

for a senior

Compare LTW vs CTW mechanics and when each fits.

for a principal

Challenge the requirement first, then reason about weaving trade-offs (ops, blast radius, performance, debuggability) and preserve aspect portability across engines.

## When the subset isn't enough ### The hard constraint Spring AOP = proxies = **method-execution join points only**. The designators for field access (`get`, `set`), object construction (`initialization`, `preinitialization`), static setup (`staticinitialization`), exception handlers (`handler`), and call sites (`call`) have **no** proxy-observable join point. Attempting them yields `IllegalArgumentException` at context startup. No property, no `proxyTargetClass` flag, no configuration removes this ceiling — it is architectural. ### The escalation path: full AspectJ Spring integrates with AspectJ's real weaver, which supports the **entire join-point model**. Two flavors: **1. Load-Time Weaving (LTW)** - Enable with `@EnableLoadTimeWeaving` (or `<context:load-time-weaver/>`). - Requires the AspectJ weaving agent: `-javaagent:/path/aspectjweaver.jar` (Spring can also use an instrumentation-capable ClassLoader in some containers). - Configure which aspects/types to weave in `META-INF/aop.xml`. - Weaving happens as classes load, so **any** class — Spring bean or not — can be advised, including field and constructor join points. **2. Compile-Time / Post-Compile Weaving (CTW)** - Use the `aspectj-maven-plugin` (or Gradle equivalent) with the AspectJ compiler `ajc`. - Advice is woven into `.class` files at build time — zero startup agent, best runtime performance. - Also supports the full join-point model. ### What you unlock - `set(* com.acme.Account.balance)` — audit every balance mutation. - `execution(com.acme.Order.new(..))` / `initialization(...)` — intercept construction. - `call(...)` — caller-side interception, and no more self-invocation blind spot. - `cflow`/`cflowbelow` — control-flow-scoped advice. ### The trade-offs a principal must weigh 1. **Operational complexity**: LTW needs a JVM agent wired into every runtime (local, CI, containers, prod) — an ops burden and a footgun if forgotten. 2. **Blast radius**: weaving can touch non-Spring, even JDK-adjacent classes; `aop.xml` scoping discipline becomes critical. 3. **Performance**: CTW is fastest at runtime; LTW adds class-load overhead; both beat nothing at request time vs proxy indirection, but broad field-level advice can be costly. 4. **Observability/debugging**: woven code makes stack traces and reasoning harder than explicit proxies. 5. **Build coupling**: CTW ties you to `ajc` and plugin config. ### The pragmatic recommendation First **challenge the requirement**: can the cross-cutting concern be met with method-execution advice (e.g., route all balance changes through a setter method and advise the method)? That keeps you in cheap, well-understood Spring AOP. Only when field/constructor/call interception is genuinely irreducible do you adopt AspectJ — preferring **CTW** for services you control (no runtime agent) and **LTW** when you must weave classes you can't recompile. Because Spring honors `@Aspect` under both engines, the aspects themselves largely carry over; what changes is the weaving infrastructure and the now-legal expanded designator set.

  • Between load-time and compile-time weaving, which would you default to and why?
    Prefer compile-time (aspectj-maven-plugin/ajc) for code you build yourself: no runtime agent to deploy everywhere, best runtime performance, weaving verified at build. Reach for load-time weaving only when you must advise classes you can't recompile.
  • Before switching engines, how might you satisfy a field-audit requirement while staying in Spring AOP?
    Funnel state changes through methods (setters/domain methods) and advise those method executions with @annotation or execution pointcuts. If all mutation goes through advisable methods, you avoid field join points entirely and keep the simpler proxy model.

saying these in an interview costs you the question

  • Believing a config flag can make Spring AOP intercept fields
  • Recommending AspectJ without acknowledging agent/blast-radius trade-offs
  • Not first checking whether method-execution advice could meet the need

context