Understanding Spring Transaction Proxies: JDK Dynamic Proxy and CGLIB
This article explains how Spring implements declarative transaction management using proxies, compares JDK dynamic proxies and CGLIB, demonstrates their behavior with code examples, and clarifies which method modifiers support transactional annotations in Spring applications.
The author presents a conversational interview scenario to illustrate how Spring's declarative transaction management works through proxy mechanisms, emphasizing the difference between JDK dynamic proxies and CGLIB-generated proxies.
Spring adds transactional behavior by creating a proxy around the target bean. When a method is annotated with @Transactional, the proxy intercepts the call, starts a transaction before delegating to the target method, and commits or rolls back after execution.
JDK Dynamic Proxy Example
```java interface Service { void doNeedTx(); void doNotneedTx(); } class ServiceImpl implements Service { @Transactional @Override public void doNeedTx() { System.out.println("execute doNeedTx in ServiceImpl"); } // no annotation @Override public void doNotneedTx() { this.doNeedTx(); } } class ProxyByJdkDynamic implements Service { private Service target; public ProxyByJdkDynamic(Service target) { this.target = target; } @Override public void doNeedTx() { System.out.println("-> create Tx here in Proxy"); target.doNeedTx(); System.out.println("<- commit Tx here in Proxy"); } @Override public void doNotneedTx() { target.doNotneedTx(); } } ```
The test output shows that calls to doNeedTx trigger transaction start and commit, while calls to doNotneedTx (which internally invokes doNeedTx) do not start a transaction because the internal call bypasses the proxy.
CGLIB Proxy Example
```java class Target { @Transactional public void doNeedTx() { System.out.println("execute doNeedTx in Target"); } public void doNotneedTx() { this.doNeedTx(); } } class ProxyByCGLIB extends Target { private Target target; public ProxyByCGLIB(Target target) { this.target = target; } @Override public void doNeedTx() { System.out.println("-> create Tx in Proxy"); target.doNeedTx(); System.out.println("<- commit Tx in Proxy"); } @Override public void doNotneedTx() { target.doNotneedTx(); } } ```
Testing reveals that CGLIB proxies also rely on public methods; only public methods can have transactional behavior because CGLIB creates a subclass in the same package and overrides public methods to insert transaction logic. Private, final, static, and package‑protected methods cannot be intercepted.
The article concludes that regardless of proxy type—JDK dynamic proxy or CGLIB—only public methods annotated with @Transactional are eligible for declarative transaction management, and the transaction is only applied when the method is invoked through the proxy, not via internal self‑calls.
Overall, the piece provides a step‑by‑step explanation of Spring's proxy‑based transaction mechanism, code samples, and practical observations that help developers understand why certain transactional annotations may appear ineffective.
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 Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java 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.
