As an architect, how do you decide whether to stay on Spring AOP or adopt full AspectJ? What are the trade-offs?
answer
- Proxy = default, AspectJ = escalation
- Switch for field/constructor/non-bean/final/self-invocation/perf
- Prefer LTW over changing the compiler
- Costs: agent, build speed, debugging, double-weaving
- Try bean-extraction before AspectJ
basics
~20 sDefault to Spring AOP: it is simple, needs no build or JVM changes, and covers transactions, security, and method logging on beans. Switch to full AspectJ only when you genuinely need what proxies cannot do — field/constructor join points, advising non-Spring or final objects, reliable self-invocation, or lower per-call overhead — and can accept the extra build/agent complexity.
solid answer
~40 sTreat Spring AOP as the default because it costs nothing operationally — no compiler change, no javaagent, pure runtime proxies — and satisfies the overwhelming majority of cross-cutting needs (`@Transactional`, `@PreAuthorize`, method metrics/logging on beans). I move to full AspectJ only when a concrete requirement escapes the proxy model: advising **field access or constructors**, weaving **non-Spring objects / third-party or final classes**, eliminating the **self-invocation** gap, or removing **per-call proxy overhead** in a hot path. Even then I prefer **load-time weaving** (agent + `@EnableLoadTimeWeaving` + `aop.xml`) over changing the compiler, unless runtime cost demands compile-time weaving. The trade-offs to weigh: build/JVM complexity, slower builds or class-load, harder debugging, javaagent availability on the deploy platform, team familiarity, and risk of accidental double-weaving. I'd also scope AspectJ narrowly via `aop.xml`/pointcuts rather than weaving everything.
go deeper
Knows Spring AOP is the usual choice; likely can't articulate switch criteria.
Names a couple of AspectJ triggers (fields, non-beans) but light on operational costs.
Gives a balanced trigger/cost list and prefers refactoring before escalating.
Owns a decision procedure and governance: standardizes a model, validates platform (javaagent), scopes weaving, and defaults to the simplest option that meets the need.
## Framing: proxy is the default, AspectJ is an escalation **Spring AOP (runtime proxies)** is deliberately the low-friction default: no special compiler, no JVM agent, no extra config beyond `@Aspect` beans. It handles the canonical cross-cutting concerns — declarative transactions, method security, caching, metrics, logging — all of which live on **Spring bean method executions**. You should stay here unless a requirement provably cannot be met. ## Triggers to switch to full AspectJ 1. **Join points proxies can't reach:** you must advise **field get/set**, **constructors**, **static** methods, or **object initialization**. Proxies have no mechanism for these. 2. **Targets that aren't beans:** you need to weave **domain objects created with `new`**, **third-party library classes**, or **final** classes/methods that CGLIB can't subclass. 3. **Self-invocation must work reliably:** if internal `this.method()` calls must be advised (pervasive in the codebase) and refactoring bean boundaries or self-injection is impractical, AspectJ's proxy-free model solves it structurally. 4. **Performance-sensitive hot paths:** proxy indirection and the advice chain add per-call cost; compile-time-woven advice is inlined with near-zero overhead. Only matters at high call volumes — measure first. 5. **Very fine-grained or complex pointcuts:** designators like `cflow`, `withincode`, `call` are AspectJ-only. ## Costs / trade-offs of AspectJ - **Build or JVM complexity:** CTW needs the `ajc` compiler in the pipeline; LTW needs a `-javaagent` on the *real* JVM launch — awkward on some PaaS/container platforms (a real deployment constraint). - **Slower feedback:** ajc builds are slower; LTW adds per-class-load cost at startup. - **Debuggability:** woven bytecode is harder to step through; stack traces and tooling get murkier. - **Team knowledge:** far fewer engineers know AspectJ's language/model; maintenance risk. - **Double-weaving hazards:** mixing Spring AOP and AspectJ can advise the same join point twice. - **Scope creep:** weaving broadly (`within *`) is easy to get wrong; keep `aop.xml`/pointcuts tight. ## A decision procedure 1. Can the concern be expressed as advice on a **bean's public method**? If yes -> **Spring AOP**. Done. 2. If not, is the blocker just **self-invocation**? Try **refactoring to separate beans** or **self-injection** first — often cheaper than AspectJ. 3. If you truly need field/constructor/non-bean/final reach -> **AspectJ**. Prefer **LTW** (no compiler change) unless you control the build and need CTW's runtime performance. 4. Whichever you pick, **scope narrowly**, document the weaving model, and guard against double-weaving. ## Governance angle (principal-level) Standardize one model per service; don't let teams mix proxy and AspectJ ad hoc. Verify the deploy platform supports a `-javaagent` before mandating LTW. Prefer refactoring (bean extraction) over weaving gymnastics when it removes the need entirely — the simplest system that meets the requirement wins.
- A team wants AspectJ purely to fix one self-invocation bug. What do you advise?Push back — extract the inner method into its own bean or use self-injection first. Adopting AspectJ for a single self-call adds disproportionate build/deploy/debug complexity for the whole service.
- You mandate load-time weaving but deploys break on the platform. Likely cause?The -javaagent (spring-instrument) is not applied to the real JVM launch — e.g., a container/PaaS that ignores your start command or needs JAVA_TOOL_OPTIONS. Verify agent attachment before standardizing LTW.
saying these in an interview costs you the question
- Adopting AspectJ by default 'because it's more powerful'
- Switching to AspectJ to fix a single self-invocation instead of refactoring
- Ignoring that -javaagent must be present on the deploy platform
- Not considering double-weaving when mixing both models
- Assuming AspectJ performance benefits without measuring the hot path