Understanding Proxy Pattern and Spring Transaction Management
The article explains static and dynamic proxy patterns, demonstrates their Java implementations, and shows how Spring’s transaction management relies on dynamic AOP proxies—highlighting that internal calls within the same class bypass the proxy, preventing @Transactional methods from starting or rolling back transactions.
This article reviews the proxy pattern and the underlying principles of Spring transaction management, illustrating why a transaction may not be effective when a non‑transactional method in the same class invokes a transactional method.
Introduction
When developing business code, a common mistake is calling a @Transactional method from another method of the same class. Because Spring creates a dynamic proxy for the bean, the internal call bypasses the proxy, so the transaction does not start.
Static Proxy Example
The article first explains static proxy using a homework analogy. The Homework interface and its implementations ( XiaoHong , XiaoWang , XiaoZhang ) demonstrate how a proxy class adds extra behavior before and after delegating to the real object.
public interface Homework {
void doHomework();
}
public class XiaoHong implements Homework {
@Override
public void doHomework() {
System.out.println("XiaoHong did homework casually");
}
}
public class XiaoWang implements Homework {
private Homework homework;
public XiaoWang(Homework homework) { this.homework = homework; }
@Override
public void doHomework() {
doStudy();
homework.doHomework();
System.out.println("XiaoWang helps with MathHomework");
doCheck();
}
private void doStudy() { System.out.println("XiaoWang is studying---------------"); }
private void doCheck() { System.out.println("XiaoWang is checking-----------------"); }
}
public class XiaoZhang implements Homework {
private Homework homework;
public XiaoZhang(Homework homework) { this.homework = homework; }
@Override
public void doHomework() {
doStudy();
homework.doHomework();
System.out.println("XiaoZhang helps with EngHomework");
doCheck();
}
private void doStudy() { System.out.println("XiaoZhang is studying---------------"); }
private void doCheck() { System.out.println("XiaoZhang is checking-----------------"); }
}
public static void main(String[] args) {
XiaoHong xiaoHong = new XiaoHong();
XiaoZhang xiaoZhang = new XiaoZhang(xiaoHong);
XiaoWang xiaoWang = new XiaoWang(xiaoHong);
xiaoZhang.doHomework();
xiaoWang.doHomework();
}Running the program prints the extra study and check messages, showing how static proxy adds behavior.
Dynamic Proxy
Static proxy becomes cumbersome when many interfaces need proxying. Dynamic proxy generates proxy classes at runtime. Two implementations are shown: JDK dynamic proxy (requires an interface) and Cglib dynamic proxy (subclassing the target class).
// JDK dynamic proxy example
public class RobotProxy implements InvocationHandler {
private Object target;
public RobotProxy(Object target) { this.target = target; }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doStudy();
Object result = method.invoke(target, args);
doCheck();
return result;
}
private void doStudy() { System.out.println("Robot is studying------------"); }
private void doCheck() { System.out.println("Robot is checking------------"); }
}
// Usage
Homework homework = (Homework) Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[]{Homework.class},
new RobotProxy(new XiaoHong())
);
homework.doHomework(); // Cglib dynamic proxy example
public class RobotV2Proxy implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
doStudy();
Object result = proxy.invokeSuper(obj, args);
doCheck();
return result;
}
private void doStudy() { System.out.println("RobotV2 is studying------------"); }
private void doCheck() { System.out.println("RobotV2 is checking------------"); }
}
// Usage
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(XiaoHong.class);
enhancer.setCallback(new RobotV2Proxy());
XiaoHong proxy = (XiaoHong) enhancer.create();
proxy.doHomework();Both dynamic proxies execute the extra logic before and after the target method without requiring explicit proxy classes for each interface.
Spring Transaction Management
Spring’s transaction management also relies on dynamic proxy (AOP). When a method annotated with @Transactional is invoked, Spring creates a proxy that opens a transaction before method execution and commits or rolls back after execution.
@Transactional
public Boolean confirmPurchaseOrder(Long taobaoUserId, String bizOrderId) throws TCException {
logger.warn("|ConfirmPurchaseOrder|UserId:" + taobaoUserId + ",orderId:" + bizOrderId);
// ...
return true;
}If batchConfirmPurchaseOrders (non‑transactional) calls confirmPurchaseOrder within the same class, the call bypasses the proxy, so the transaction is never started and rollback does not occur.
The article then dives into the core Spring classes involved:
PlatformTransactionManager – defines getTransaction , commit , and rollback .
TransactionInterceptor – implements MethodInterceptor and invokes invokeWithinTransaction around the target method.
DynamicAdvisedInterceptor – part of Cglib AOP proxy, builds the interceptor chain.
Key snippets from Spring’s source illustrate how the transaction attribute is retrieved, how the transaction is started, and how commit/rollback are performed based on method outcome.
protected Object invokeWithinTransaction(Method method, @Nullable Class
targetClass, InvocationCallback invocation) throws Throwable {
TransactionAttribute txAttr = ...; // obtain @Transactional metadata
PlatformTransactionManager ptm = asPlatformTransactionManager(determineTransactionManager(txAttr));
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, methodIdentification(...));
try {
Object retVal = invocation.proceedWithInvocation();
commitTransactionAfterReturning(txInfo);
return retVal;
} catch (Throwable ex) {
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
} finally {
cleanupTransactionInfo(txInfo);
}
}Finally, the article summarizes that Spring uses AOP to generate a proxy for @Transactional methods. Calls that do not go through the proxy (e.g., internal method calls) will not participate in transaction management.
Conclusion
Understanding static vs. dynamic proxy clarifies why Spring’s transaction management works and why internal method calls can break transaction boundaries. Dynamic proxy (JDK or Cglib) enables Spring to weave cross‑cutting concerns such as transaction handling without modifying business code.
DaTaobao Tech
Official account of DaTaobao Technology
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.