Spring 事务传播机制

xiaopeng
发布于 2026-08-18 / 1 阅读
0
0

Spring 事务传播机制

Spring 事务传播机制 ⭐⭐⭐⭐⭐

面试频率:★★★★★
工作频率:★★★★★


⚡30 秒速记

🟢 什么是事务传播?

一个事务方法调用另一个事务方法时,决定被调用方法应该加入当前事务,还是创建新事务。

最重要的两个:

REQUIRED

外面有事务 → 加入
外面没事务 → 创建

默认传播行为
REQUIRES_NEW

不管外面有没有事务 → 都创建新事务

外面有事务 → 先挂起外层事务

一、为什么需要事务传播?

例如:

@Transactional
public void createOrder() {

    saveOrder();

}

saveOrder() 也有事务:

@Transactional
public void saveOrder() {

}

问题:

createOrder已经有事务

↓

调用saveOrder

↓

saveOrder怎么办?

它可以:

加入createOrder的事务

也可以:

自己重新创建一个事务

Spring 用:

Propagation

决定这个行为。

这就是:

事务传播机制。


二、REQUIRED ⭐⭐⭐⭐⭐

REQUIRED 是:

Spring 默认的事务传播行为。

所以:

@Transactional

默认相当于:

@Transactional(
    propagation = Propagation.REQUIRED
)

REQUIRED 规则

外面有事务

↓

加入外面的事务


外面没有事务

↓

创建一个新事务

三、REQUIRED 举例

例如:

@Transactional
public void createOrder() {

    saveOrder();

}

另一个 Bean 中:

@Transactional
public void saveOrder() {

}

假设调用经过 Spring 事务代理。

执行:

调用createOrder
      ↓
开启事务A
      ↓
调用saveOrder
      ↓
发现已经存在事务A
      ↓
加入事务A

最终:

createOrder

+

saveOrder

↓

事务A

也就是说:

两个方法属于同一个事务。


如果:

saveOrder发生异常

导致事务回滚:

事务A整体回滚

所以:

createOrder的数据库操作

+

saveOrder的数据库操作

↓

一起回滚

四、REQUIRES_NEW ⭐⭐⭐⭐⭐

使用:

@Transactional(
    propagation = Propagation.REQUIRES_NEW
)

意思:

每次都需要一个新的事务。

如果外层已经存在事务:

挂起外层事务

↓

创建新事务

例如:

createOrder

↓

事务A

调用:

saveLog

而 saveLog:

@Transactional(
    propagation = Propagation.REQUIRES_NEW
)
public void saveLog() {

}

执行流程:

事务A正在执行
      ↓
调用saveLog
      ↓
发现REQUIRES_NEW
      ↓
挂起事务A
      ↓
创建事务B
      ↓
执行saveLog
      ↓
提交事务B
      ↓
事务B结束
      ↓
恢复事务A
      ↓
继续执行createOrder

所以:

createOrder → 事务A

saveLog     → 事务B

两个事务:

相互独立。


五、REQUIRED 和 REQUIRES_NEW 区别 ⭐⭐⭐⭐⭐

REQUIRED

事务A
  ↓
调用B
  ↓
B加入事务A

最终:

A + B

↓

同一个事务

REQUIRES_NEW

事务A
  ↓
调用B
  ↓
挂起事务A
  ↓
创建事务B
  ↓
执行B
  ↓
提交事务B
  ↓
恢复事务A

最终:

A → 事务A

B → 事务B

一张图记住

REQUIRED:

A事务
  ↓
  B
  ↓
加入A事务

A + B = 一个事务
REQUIRES_NEW:

A事务
  ↓
挂起A
  ↓
创建B事务
  ↓
B事务提交
  ↓
恢复A

A、B = 两个事务

六、REQUIRES_NEW 有什么用?

一个非常典型的场景:

保存操作日志 / 失败记录。

例如:

@Transactional
public void createOrder() {

    saveOrder();

    logService.saveLog();

    throw new RuntimeException();

}

需求:

订单失败

↓

订单数据回滚

但是:

操作日志必须保留

七、如果 saveLog 使用 REQUIRED

例如:

@Transactional
public void saveLog() {

}

因为默认:

REQUIRED

所以:

createOrder
      ↓
事务A
      ↓
saveOrder
      ↓
saveLog
      ↓
也加入事务A

最后:

throw new RuntimeException();

事务A:

回滚

结果:

订单 ❌ 回滚

日志 ❌ 也回滚

问题:

本来就是为了记录失败日志,结果日志自己也没了。


八、使用 REQUIRES_NEW

修改:

@Transactional(
    propagation = Propagation.REQUIRES_NEW
)
public void saveLog() {

}

执行:

createOrder
      ↓
事务A
      ↓
保存订单
      ↓
调用saveLog
      ↓
挂起事务A
      ↓
创建事务B
      ↓
保存日志
      ↓
提交事务B
      ↓
恢复事务A
      ↓
createOrder发生异常
      ↓
事务A回滚

最终:

订单:

回滚 ❌


日志:

已经提交 ✅

因为:

订单 → 事务A

日志 → 事务B

是两个独立事务。


九、REQUIRES_NEW 最大的坑:自调用 ⭐⭐⭐⭐⭐

例如:

@Service
public class OrderService {

    @Transactional
    public void createOrder() {

        saveLog();

    }


    @Transactional(
        propagation = Propagation.REQUIRES_NEW
    )
    public void saveLog() {

    }

}

你可能认为:

createOrder
      ↓
事务A
      ↓
saveLog
      ↓
REQUIRES_NEW
      ↓
事务B

但是:

可能不会按照预期创建事务B。


十、为什么 REQUIRES_NEW 可能失效?

因为:

saveLog();

实际上相当于:

this.saveLog();

这是:

同一个对象内部调用。

你前面学过:

@Transactional

↓

依赖Spring AOP代理

正常调用:

外部调用
   ↓
Spring代理对象
   ↓
发现@Transactional
   ↓
事务拦截器
   ↓
创建事务
   ↓
执行目标方法

但是自调用:

OrderService代理
      ↓
createOrder()
      ↓
原始OrderService
      ↓
this.saveLog()

saveLog()

没有重新经过:

Spring代理对象

所以:

Spring 没机会读取:

@Transactional(
    propagation = Propagation.REQUIRES_NEW
)

也就不会按预期:

挂起事务A

↓

创建事务B

十一、实际项目怎么解决?

最简单常见的方式:

拆成另外一个 Spring Bean。

例如:

@Service
public class LogService {

    @Transactional(
        propagation = Propagation.REQUIRES_NEW
    )
    public void saveLog() {

        // 保存日志

    }

}

OrderService:

@Service
public class OrderService {

    @Autowired
    private LogService logService;


    @Transactional
    public void createOrder() {

        saveOrder();

        logService.saveLog();

    }

}

这时候:

调用OrderService代理
       ↓
createOrder
       ↓
开启事务A
       ↓
调用LogService
       ↓
经过LogService代理
       ↓
发现REQUIRES_NEW
       ↓
挂起事务A
       ↓
创建事务B
       ↓
执行saveLog
       ↓
提交事务B
       ↓
恢复事务A

这样:

REQUIRES_NEW 才能正常通过代理发挥作用。


十二、和 Spring AOP 串起来 ⭐⭐⭐⭐⭐

现在你前面学的:

Spring AOP

和事务传播可以串起来:

@Transactional
      ↓
Spring发现事务注解
      ↓
创建AOP代理
      ↓
调用代理对象
      ↓
事务拦截器
      ↓
读取Propagation
      ↓
决定事务怎么传播

如果:

REQUIRED

那么:

有事务 → 加入

如果:

REQUIRES_NEW

那么:

有事务 → 挂起

↓

创建新事务

十三、Spring 一共有 7 种事务传播行为

REQUIRED

REQUIRES_NEW

SUPPORTS

MANDATORY

NOT_SUPPORTED

NEVER

NESTED

面试优先掌握:

REQUIRED ⭐⭐⭐⭐⭐

REQUIRES_NEW ⭐⭐⭐⭐⭐

其他几个:

知道即可。


十四、SUPPORTS

意思:

外面有事务

↓

加入事务


外面没事务

↓

不用事务

简单理解:

有就用,没有也无所谓。


十五、MANDATORY

意思:

必须存在外层事务

如果:

外面没有事务

直接:

抛异常

十六、NOT_SUPPORTED

意思:

不使用事务。

如果外面有事务:

挂起外层事务

↓

自己不用事务执行

↓

执行完成

↓

恢复外层事务

十七、NEVER

意思:

禁止存在事务。

如果:

外面有事务

直接:

抛异常

十八、NESTED

意思:

嵌套事务。

可以粗略理解:

外层事务A
    ↓
创建保存点 Savepoint
    ↓
执行内部事务B

B出现异常时:

可以:

回滚到保存点

而不一定直接结束整个外层事务。

注意:

NESTED

≠

REQUIRES_NEW

十九、NESTED 和 REQUIRES_NEW 区别

REQUIRES_NEW

事务A
  ↓
挂起A
  ↓
创建独立事务B

A、B:

两个独立事务

NESTED

事务A
  ↓
Savepoint
  ↓
事务B

B:

属于外层事务中的:

嵌套执行

不是完全独立的新事务。


二十、7种传播行为速记

传播行为外面有事务外面没事务
REQUIRED加入创建
REQUIRES_NEW挂起外层,创建新的创建
SUPPORTS加入不使用事务
MANDATORY加入报错
NOT_SUPPORTED挂起不使用事务
NEVER报错不使用事务
NESTED嵌套执行创建

最重要:

REQUIRED

有就加入
没就创建
REQUIRES_NEW

永远要新事务

💼 工作场景

场景1:普通业务

例如:

创建订单

↓

保存订单明细

↓

修改订单状态

希望:

全部成功

或者

全部失败

通常:

REQUIRED

大家加入同一个事务。


场景2:独立日志

例如:

订单操作失败

但是:

失败日志必须保存

可以考虑:

REQUIRES_NEW

让日志使用:

独立事务

🎤 面试回答(30秒)

Spring 事务传播机制用于解决一个事务方法调用另一个事务方法时,被调用方法应该如何处理事务的问题。

默认传播行为是 REQUIRED,如果当前存在事务就加入当前事务,如果不存在就创建新事务。

REQUIRES_NEW 则无论当前是否存在事务都会使用一个新的事务,如果外层已经有事务,会先挂起外层事务,内部新事务执行完成后再恢复外层事务。

另外事务传播依赖 Spring AOP 代理,所以同类内部自调用可能导致传播行为无法按预期生效。


🎯 面试追问

Q1:什么是事务传播?

答:

一个事务方法调用另一个事务方法时,决定内部方法如何处理事务。


Q2:@Transactional 默认传播行为是什么?

答:

Propagation.REQUIRED

Q3:REQUIRED 是什么意思?

答:

有事务 → 加入

没事务 → 创建

Q4:REQUIRES_NEW 是什么意思?

答:

有事务 → 挂起外层事务 → 创建新事务

没事务 → 创建新事务

Q5:REQUIRED 和 REQUIRES_NEW 最大区别?

答:

REQUIRED:

和外层共用一个事务

REQUIRES_NEW:

创建独立的新事务

Q6:为什么保存日志可能使用 REQUIRES_NEW?

答:

为了让日志事务和主业务事务相互独立。

即使:

主业务回滚

日志已经提交:

仍然可以保留

Q7:为什么 REQUIRES_NEW 可能失效?

答:

如果:

this.saveLog();

同类内部调用:

没有经过 Spring AOP 代理。

所以:

事务拦截器无法处理:

Propagation.REQUIRES_NEW

Q8:怎么解决自调用问题?

答:

一种常见方式:

把需要独立事务的方法:

拆到另外一个Spring Bean

通过另一个 Bean 的代理对象调用。


Q9:REQUIRES_NEW 和 NESTED 一样吗?

答:

不一样。

REQUIRES_NEW

↓

独立新事务
NESTED

↓

基于外层事务进行嵌套执行
通常涉及Savepoint

⚠️ 工作踩坑

1. 同类调用 REQUIRES_NEW

例如:

this.saveLog();

可能绕过:

Spring代理

导致:

事务传播行为没有按预期生效。


2. 认为加了 @Transactional 就一定有事务

错误。

还要考虑:

是不是Spring Bean

↓

是不是通过代理调用

↓

有没有自调用

↓

异常是否符合回滚规则

3. REQUIRES_NEW 不是越多越好

REQUIRES_NEW:

会创建独立事务。

如果大量使用:

事务数量增加

数据库连接占用增加

业务逻辑复杂度增加

所以:

只在真正需要:

独立提交 / 独立回滚

的业务场景使用。


❓ 自测

  1. 什么是事务传播机制?

  2. Spring 默认事务传播行为是什么?

  3. REQUIRED 是什么意思?

  4. REQUIRES_NEW 是什么意思?

  5. REQUIRED 和 REQUIRES_NEW 最大区别是什么?

  6. 外层存在事务时,REQUIRES_NEW 会怎么处理?

  7. 为什么操作日志可能使用 REQUIRES_NEW?

  8. 外层事务回滚,已经提交的 REQUIRES_NEW 内层事务会一起回滚吗?

  9. 为什么同类内部调用可能导致 REQUIRES_NEW 失效?

  10. 事务传播机制和 Spring AOP 有什么关系?

  11. Spring 有哪 7 种事务传播行为?

  12. SUPPORTS 是什么意思?

  13. MANDATORY 是什么意思?

  14. NOT_SUPPORTED 是什么意思?

  15. NEVER 是什么意思?

  16. NESTED 和 REQUIRES_NEW 有什么区别?


评论