Why Spring Transactions Fail with JDK Dynamic Proxies and How to Fix Them
This article examines how JDK dynamic proxies cause Spring transaction methods to lose their transactional behavior, illustrates the issue with four concrete scenarios, and presents three practical solutions to ensure proper rollback and commit semantics.
1. Scenario Analysis
We set up two transactions, parent() and child(), and call them from a controller. Four scenarios (A‑D) demonstrate different outcomes when child() throws an exception.
Scenario A and B execute both methods successfully, inserting data in both tables.
In Scenario C the child() method is modified to throw an exception without being caught; both transactions roll back and no data is inserted.
Scenario D catches the exception in parent(). Both parent() and child() commit successfully, contrary to the expectation that only child() should roll back.
2. Root Cause
Spring transaction management relies on JDK dynamic proxies (or CGLIB). When parent() calls child() directly, the call bypasses the proxy, so the transactional annotation on child() is ignored. Consequently, in Scenario C the exception propagates to parent() causing a full rollback, and in Scenario D the caught exception prevents any rollback, so both methods commit.
Illustration of the proxy mechanism:
3. How the Proxy Works
Only the method invoked on the proxy object itself goes through the invoke() interceptor. Internal method calls (e.g., this.child()) are plain Java calls and do not trigger the proxy. proxy Therefore, transactional behavior is applied only when the call originates from the proxy.
4. Solutions
1. Refactor code to avoid internal transactional calls; place transactional boundaries at a higher service level.
2. Expose the proxy and obtain it via AopContext.currentProxy() (Spring Boot: @EnableAspectJAutoProxy(exposeProxy = true)). <aop:aspectj-autoproxy expose-proxy="true"/> 3. Retrieve the bean from ApplicationContext and invoke the method on that bean, ensuring the call goes through the proxy.
5. Summary
JDK dynamic proxies cause Spring transactions to be ineffective when a transactional method calls another transactional method within the same class. Understanding this proxy limitation and applying one of the three remedies prevents unexpected rollbacks and ensures correct transaction semantics.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
