How does switching Spring's transaction management to AspectJ weaving mode eliminate the self-invocation limitation, and what does it cost?
answer
- weaving = advice baked into bytecode
- no proxy object to bypass
- CTW (ajc) vs LTW (java agent)
- spring-aspects AnnotationTransactionAspect
- also fixes private/final methods; global + build cost
basics
~20 sAspectJ weaves the transaction advice directly into the class bytecode instead of wrapping the bean in a proxy. Since there's no separate proxy object, even this.method() self-calls are intercepted. Cost: a weaver agent or build step.
solid answer
~50 sThe default proxy mode wraps each bean in a separate proxy object, so only calls entering through that proxy get advised — self-calls slip past. AspectJ mode instead **weaves** the transaction advice into the target class's own bytecode (`@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)` plus `spring-aspects`' `AnnotationTransactionAspect`). There is no wrapper object; the advice becomes part of the method body, so every invocation — external, self, even private/final — triggers it. Weaving happens either at compile time (the AspectJ compiler / ajc) or at class load with load-time weaving via a Java agent (`spring-instrument`, `@EnableLoadTimeWeaving`). Costs: you must add the weaver (agent or build plugin), it complicates the build and debugging, applies globally rather than surgically, and diverges from the mainstream proxy path most Spring devs know. I'd reach for it only when self-invocation or non-public transactional methods are pervasive across the codebase.
code
java · 20 lines@Configuration
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
// For load-time weaving also add @EnableLoadTimeWeaving and run with
// -javaagent:spring-instrument-<version>.jar
public class TxConfig {
@Bean
public PlatformTransactionManager txManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
// Now this works even via self-invocation, because the advice is woven
// into OrderService's own bytecode (needs spring-aspects on the classpath):
@Service
class OrderService {
@Transactional void placeOrder() { audit(); } // self-call
@Transactional(propagation = Propagation.REQUIRES_NEW)
void audit() { /* new tx DOES start under AspectJ weaving */ }
}go deeper
Not expected to know AspectJ mode in depth.
Should know AspectJ mode exists and roughly that it weaves rather than proxies.
Should explain weaving vs proxying, CTW vs LTW, and the setup cost.
Should judge when the global tradeoff is worth it vs surgical fixes, and account for build/agent/observability impact.
## Proxy mode vs AspectJ mode Spring can apply `@Transactional` two ways, selected by `@EnableTransactionManagement(mode = ...)`: - **`AdviceMode.PROXY`** (the default) — Spring creates a runtime proxy (JDK dynamic proxy or CGLIB subclass) that wraps the bean. Advice runs only on calls that enter *through the proxy*. Self-invocation, `private`, `final`, and `static` methods are not advised. - **`AdviceMode.ASPECTJ`** — Spring uses the AspectJ aspect `AnnotationTransactionAspect` (shipped in the `spring-aspects` module) and relies on **weaving** to insert the transaction advice directly into the compiled bytecode of your classes. ## What 'weaving' means Weaving = editing the bytecode so the aspect's advice is embedded inside the affected methods. Because the advice is now literally part of `record()`'s method body, it fires no matter *how* `record()` is reached — including `this.record()`. There is no separate proxy object to bypass; the object *is* the advised object. Two weaving strategies: - **Compile-time weaving (CTW)** — use the AspectJ compiler (`ajc`) / the AspectJ Maven or Gradle plugin to weave at build time. Fastest at runtime, no agent, but changes the build toolchain. - **Load-time weaving (LTW)** — weave when classes are loaded by the JVM, using a Java agent: run with `-javaagent:spring-instrument-<version>.jar` and enable `@EnableLoadTimeWeaving` (with a `META-INF/aop.xml`). No special compiler, but requires the agent on the command line and adds class-load overhead. ## Configuration sketch ```java @Configuration @EnableTransactionManagement(mode = AdviceMode.ASPECTJ) @EnableLoadTimeWeaving // only for LTW public class TxConfig { @Bean DataSourceTransactionManager txManager(DataSource ds) { return new DataSourceTransactionManager(ds); } } ``` Require `org.springframework:spring-aspects` on the classpath (it provides `AnnotationTransactionAspect`). For LTW, launch with the `spring-instrument` agent. ## Why it removes the limitation Since advice is woven into the class itself: - **Self-invocation** (`this.method()`) is advised — the whole point. - **`private` / `protected` / package-private** methods can be transactional (proxies can only advise `public`, or `public`+`protected` for CGLIB — but never truly private). - **`final` classes/methods** work (CGLIB cannot subclass `final`). ## The costs / downsides - **Tooling** — you must integrate the AspectJ compiler (CTW) or ship and configure a Java agent (LTW). Both are extra moving parts and a common source of misconfiguration. - **Global, not surgical** — it changes how *all* transactional advice is applied. You can't scope it to one problem class. - **Debugging/observability** — woven bytecode is harder to reason about; stack traces and step-debugging differ from the familiar proxy call chain. - **Ecosystem familiarity** — most Spring code and docs assume proxy mode; teams may be surprised. - **Startup/build cost** — LTW adds class-load overhead; CTW adds build time. ## When it's justified When the codebase has widespread self-invocation, needs transactions on non-public methods, or you're already using AspectJ for other cross-cutting concerns. Otherwise, prefer the targeted proxy-mode fixes (separate bean / self-injection). ## Scope note The general AOP/weaving machinery is a broader topic; here the exam point is specifically *why AspectJ mode fixes self-invocation* (bytecode weaving, no proxy) and *what it costs*.
- What extra dependency/agent do you need for AspectJ transaction mode?The spring-aspects module (provides AnnotationTransactionAspect). For load-time weaving you also need the spring-instrument Java agent on the launch command; for compile-time weaving you need the AspectJ compiler/plugin (ajc).
- Besides self-invocation, what other proxy limitations does AspectJ weaving remove?Transactions on private, protected, and package-private methods, and on final classes/methods — all impossible under JDK/CGLIB proxy mode.
saying these in an interview costs you the question
- Saying AspectJ mode still creates a proxy per bean
- Thinking no extra dependency or agent is needed
- Claiming it can be enabled per-class rather than globally
- Confusing load-time weaving (agent) with compile-time weaving (ajc)