spring的事务传播机制
2. 场景举例说明
假设我们有以下两个服务方法:
- 方法 A(调用方/外层):
@Transactional(propagation = Propagation.REQUIRED) - 方法 B(被调用方/内层):
@Transactional(propagation = Propagation.XXX)
当 A 调用 B 时,Spring 在拦截 B 的执行前会进行判断:
表格
| 传播行为 (B的配置) | 判断逻辑:“当前是否存在事务?” | 实际含义 | B 的行为 |
|---|---|---|---|
| REQUIRED | 是 (因为 A 有事务) | 加入 A 的事务 | B 不新建事务,共用 A 的事务连接。 |
| REQUIRES_NEW | 是 (因为 A 有事务) | 挂起 A 的事务 | B 暂停 A 的事务,创建一个全新的独立事务。 |
| NESTED | 是 (因为 A 有事务) | 在 A 的事务中嵌套 | B 在 A 的事务中设置一个保存点(Savepoint)。 |
| SUPPORTS | 是 (因为 A 有事务) | 加入 A 的事务 | B 以事务方式运行,共用 A 的事务。 |
| MANDATORY | 是 (因为 A 有事务) | 校验通过 | B 正常加入 A 的事务。若 A 无事务,则抛异常。 |
| NOT_SUPPORTED | 是 (因为 A 有事务) | 挂起 A 的事务 | B 暂停 A 的事务,以非事务方式运行。 |
| NEVER | 是 (因为 A 有事务) | 校验失败 | 抛出异常,因为 NEVER 要求当前不能有事务。 |
反之,如果 A 没有加 @Transactional:
- 此时“当前存在事务”为 否。
- REQUIRED 的 B 会自己新建一个事务。
- REQUIRES_NEW 的 B 也会新建一个事务。
- NEVER 的 B 会正常以非事务方式执行。
- MANDATORY 的 B 会抛出异常(因为它强制要求必须有外层事务)。
3. 关键注意事项:代理机制的影响
虽然理论上“当前事务”由调用方决定,但在实际代码中,必须确保调用是通过 Spring AOP 代理对象进行的,否则事务传播机制完全不会生效。
-
不同类之间调用(推荐):
java@Service public class ServiceA { @Autowired private ServiceB serviceB; // 注入的是代理对象 @Transactional public void methodA() { // 这里调用 serviceB.methodB() // Spring 拦截器会检查当前线程是否有事务(由 methodA 创建) serviceB.methodB(); } }在这种情况下,
methodB能正确感知到methodA创建的事务。 -
同类内部调用(陷阱):
java@Service public class ServiceA { @Transactional public void methodA() { // 直接调用 this.methodB() // 绕过代理,Spring 拦截器不会执行 // methodB 无法感知事务上下文,传播行为失效 this.methodB(); } @Transactional(propagation = Propagation.REQUIRES_NEW) public void methodB() { ... } }在这种情况下,
methodB上的注解完全失效,它既不会加入事务,也不会新建事务,而是像普通方法一样运行。此时讨论“当前是否存在事务”对methodB的传播行为已无意义,因为传播逻辑根本没有触发。
总结
- “当前”指的是调用链路中上层方法所建立的事务上下文。
- 被调用方根据这个上下文的状态,结合自身配置的传播行为(Propagation),决定是加入、新建、挂起还是报错。
- 务必确保通过代理对象调用,否则传播机制不生效。
浙公网安备 33010602011771号