When would you choose AdviceMode.ASPECTJ over the default proxy mode for @EnableTransactionManagement, and what are the trade-offs?
answer
- PROXY = wrapper, public + external only
- ASPECTJ = weave into bytecode
- AnnotationTransactionAspect in spring-aspects
- CTW (ajc) or LTW (agent + @EnableLoadTimeWeaving)
- escape hatch for non-public / self-invocation
basics
~20 sChoose ASPECTJ when you need transactions on non-public methods or on self-invoked internal calls, which proxy mode can't advise. The cost is a weaving setup (compile-time or load-time) and more complex builds; proxy mode is simpler and the default.
solid answer
~40 sProxy mode (the default) implements @Transactional with a runtime wrapper, so it only advises public methods reached through the proxy — internal self-calls and non-public methods are silently unmanaged, and final/Kotlin-final members can't be proxied. AdviceMode.ASPECTJ instead weaves the transactional aspect (AnnotationTransactionAspect) directly into the class bytecode via compile-time weaving (aspectj compiler) or load-time weaving (@EnableLoadTimeWeaving + a Java agent). Because the advice lives in the method itself, it works on non-public methods and on self-invocation, with no proxy indirection. The trade-offs: you take on spring-aspects, weaving configuration, and slower/trickier builds; behavior becomes less obvious; and it's overkill for most apps. In practice, most teams keep proxy mode and design service boundaries so transactional units align with proxy boundaries, reaching for ASPECTJ only when they genuinely can't restructure.
code
java · 24 lines// Opt into AspectJ weaving so non-public and self-invoked @Transactional methods are honored.
@Configuration
@EnableTransactionManagement(mode = AdviceMode.ASPECTJ)
// For load-time weaving you'd also add @EnableLoadTimeWeaving and run with the AspectJ agent;
// for compile-time weaving, configure the AspectJ (ajc) build plugin instead.
public class TxConfig {
@Bean
public PlatformTransactionManager txManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
@Service
class ReportService {
@Transactional
public void run() {
buildSection(); // self-invocation -- with ASPECTJ this IS advised
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
protected void buildSection() { // protected -- ignored in PROXY mode, woven in ASPECTJ
// ...
}
}go deeper
Know only that proxy mode is the default and works on public methods.
Know ASPECTJ exists and removes the public-only/self-invocation limits.
Explain weaving (CTW/LTW), the spring-aspects dependency, and why extraction usually beats switching modes.
Make the org-level call: default PROXY, treat ASPECTJ as an escape hatch, weigh build/toolchain cost, and align service design with proxy boundaries.
## The two modes `@EnableTransactionManagement(mode = ...)` picks how `@Transactional` advice is applied. ### `AdviceMode.PROXY` (default) Spring wraps each transactional bean in a runtime **proxy** (JDK dynamic proxy for interfaces, CGLIB subclass otherwise). The advice sits *outside* the object. Consequences: - Only **public** methods are advised. - Only calls **through the proxy** are advised — internal `this.method()` self-invocation is not. - **`final`** classes/methods (and Kotlin's default-final) can't be CGLIB-subclassed → advice silently lost. - Zero build-time tooling; works out of the box; the default in Spring Boot. ### `AdviceMode.ASPECTJ` Spring uses the real AspectJ aspect **`AnnotationTransactionAspect`** (shipped in `spring-aspects`) and **weaves** the transaction logic into the bytecode of your class. Two weaving flavors: - **Compile-time weaving (CTW)** — the AspectJ compiler (`ajc`) or the AspectJ Maven/Gradle plugin weaves at build time. - **Load-time weaving (LTW)** — `@EnableLoadTimeWeaving` plus a Java agent (`-javaagent:aspectjweaver.jar` or Spring's instrumentation) weaves classes as they load. Because the advice is *inside* the woven method: - **Non-public methods** (protected/package-private, even private with full AspectJ) can be transactional. - **Self-invocation works** — an internal call still hits the woven advice. - No proxy, so no proxy-related final/interface constraints in the same way. ## Trade-offs | Aspect | PROXY | ASPECTJ | |---|---|---| | Setup | none (default) | spring-aspects + weaving (CTW build plugin or LTW agent) | | Non-public methods | ignored | advised | | Self-invocation | not advised | advised | | Build complexity | low | higher (weaver, agent, slower builds) | | Debuggability | clear (proxy in stack) | woven advice is less obvious | | Typical use | 95% of apps | special cases only | ## When ASPECTJ is justified - A legacy or framework constraint forces transactional **non-public** methods and you cannot refactor. - Heavy **self-invocation** patterns you genuinely can't restructure into separate beans. - You already run AspectJ weaving for other cross-cutting concerns and want consistency/performance (no per-call proxy dispatch). ## Why most teams stay on PROXY The proxy limitations are usually **design smells in disguise**: a method that needs its own transaction usually deserves its own collaborator bean. Extracting it (so the call crosses a proxy boundary) is simpler, more testable, and keeps the build plain. Adding a weaver for a couple of methods is a poor trade. ## Related knobs - `proxyTargetClass` — only meaningful in PROXY mode; forces CGLIB. Boot defaults it true. - `order` — advisor ordering vs other aspects; relevant in both modes but expressed differently. - Mixing modes across the app is not supported per enabler; pick one per `@EnableTransactionManagement`. ## Bottom line Default to PROXY. Treat ASPECTJ as an escape hatch for genuine non-public/self-invocation needs you can't design away, and budget for the weaving toolchain.
- What extra dependency and runtime setup does ASPECTJ mode require versus proxy mode?The spring-aspects module (which provides AnnotationTransactionAspect) plus a weaver: either compile-time weaving via the AspectJ compiler/build plugin, or load-time weaving via @EnableLoadTimeWeaving and a Java agent (aspectjweaver).
- A colleague wants ASPECTJ just to fix one self-invocation. What would you suggest instead?Extract the self-invoked method into its own injected bean so the call crosses a proxy boundary. It fixes the problem with zero build changes and usually improves the design; reserve ASPECTJ for cases you truly can't restructure.
saying these in an interview costs you the question
- Claiming ASPECTJ mode still can't advise self-invocation or non-public methods
- Thinking switching to ASPECTJ needs no extra dependencies or weaving setup
- Assuming proxyTargetClass affects ASPECTJ mode (it only matters for proxies)
- Recommending ASPECTJ as the default rather than an exception