
本文深入解析 Spring 如何通过动态代理与 TransactionInterceptor 实现声明式事务,阐明代理对象不生成真实字节码、而是基于 InvocationHandler(如 JdkDynamicAopProxy)在运行时织入事务逻辑,并重点说明事务上下文如何借助 ThreadLocal 隔离多线程场景下的 EntityManager 并发访问问题。
本文深入解析 spring 如何通过动态代理与 `transactioninterceptor` 实现声明式事务,阐明代理对象不生成真实字节码、而是基于 `invocationhandler`(如 `jdkdynamicaopproxy`)在运行时织入事务逻辑,并重点说明事务上下文如何借助 `threadlocal` 隔离多线程场景下的 `entitymanager` 并发访问问题。
Spring 的 @Transactional 注解从不生成可查看的“代理类源码”(如 .java 文件),它也不像编译期 AOP(如 AspectJ)那样生成新类字节码。所谓“代理”,本质是运行时动态构建的轻量级包装对象:对实现接口的 Bean,Spring 使用 JDK 动态代理(基于 java.lang.reflect.Proxy 和 InvocationHandler);对无接口的类,则采用 CGLIB 字节码增强(生成目标类的子类)。二者均不落地为物理文件,因此无法直接“查看代理代码”,但可通过调试或反编译观察其行为。
真正的事务逻辑入口是 TransactionInterceptor.invoke() 方法。该拦截器实现了 MethodInterceptor 接口,被植入代理对象的调用链中。当外部调用标注 @Transactional 的方法时,实际执行流程如下:
// 简化版 TransactionInterceptor.invoke 核心逻辑示意
public Object invoke(MethodInvocation invocation) throws Throwable {
Method method = invocation.getMethod();
Class> targetClass = AopUtils.getTargetClass(invocation.getThis());
// 1. 解析事务属性(来自 @Transactional 注解)
TransactionAttribute txAttr = transactionAttributeSource.getTransactionAttribute(method, targetClass);
// 2. 获取事务管理器(如 DataSourceTransactionManager)
PlatformTransactionManager ptm = determineTransactionManager(txAttr);
// 3. 开启/加入事务(关键:prepareTransactionInfo 创建 ThreadLocal 绑定)
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, methodIdentification);
Object result;
try {
// 4. 执行原始业务方法
result = invocation.proceed();
} catch (Throwable ex) {
// 5. 异常时按规则回滚(txAttr.rollbackOn(ex) 判定)
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
} finally {
// 6. 正常完成则提交,或清理事务资源
cleanupTransactionInfo(txInfo);
}
// 7. 提交事务(若未异常且满足提交条件)
commitTransactionAfterReturning(txInfo);
return result;
}
值得注意的是:Spring 绝不会 在业务方法中直接插入类似 em.getTransaction().begin() 这样的硬编码。相反,它通过 PlatformTransactionManager(如 DataSourceTransactionManager)统一管控事务生命周期。该管理器内部维护一个 ThreadLocal
- 当线程 A 调用 @Transactional 方法 → DataSourceTransactionManager.doBegin() 获取连接并绑定到当前线程的 ThreadLocal;
- 线程 B 同时调用同一方法 → 独立获取另一连接,同样绑定至自身 ThreadLocal;
- EntityManager(或 JdbcTemplate)底层通过 TransactionSynchronizationManager 获取当前线程关联的连接,从而天然避免“重复 begin”异常。
✅ 关键结论与注意事项:
- 代理不可见,但行为可追踪:启用 JVM 参数 -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 可导出 JDK 代理的 .class 文件(仅限 JDK 代理,且需调试支持);CGLIB 代理类名形如 OrderService$$EnhancerBySpringCGLIB$$a1b2c3d4,可通过 System.out.println(bean.getClass()) 查看。
- 事务失效常见陷阱:同一类内方法自调用(this.methodB())绕过代理,导致 @Transactional 不生效;务必通过 Spring 容器注入本类实例或使用 AopContext.currentProxy()。
- EntityManager 线程安全前提:@PersistenceContext 注入的 EntityManager 是容器托管的 代理对象(非原生 JPA EntityManager),其所有操作(persist, find, getTransaction())均被 TransactionSynchronizationManager 拦截并路由至当前线程绑定的事务资源,故无需也不应手动调用 getTransaction().begin() —— 这会破坏 Spring 的事务上下文隔离。
综上,Spring 的事务魔法不在“生成代码”,而在“运行时编织”与“线程级上下文隔离”。理解 TransactionInterceptor 的拦截链、PlatformTransactionManager 的资源绑定机制,以及 ThreadLocal 在事务传播中的枢纽作用,才是掌握声明式事务本质的关键。









