Java外功精要(6)——Spring事务及其传播机制

Java 外功精要(6)——Spring 事务及其传播机制(2025-2026 生产级理解版)

Spring 事务是 Java 后端工程师最常使用、也最容易踩坑的“外功”之一。
尤其是事务传播行为(Propagation),面试必考、生产必踩、代码审查必看。

下面用最清晰的结构 + 通俗比喻 + 真实场景,把 Spring 事务的核心讲透。

一、Spring 事务的核心本质(一句话)

Spring 事务 ≠ 数据库事务
Spring 事务是对数据库事务的一种抽象与管理,通过 AOP 动态代理在方法执行前后插入 begin/commit/rollback 逻辑。

最核心的两句话

  1. Spring 事务的边界 = 被 @Transactional 标注的方法(或 xml 定义的事务方法)
  2. 事务的传播行为 = 当方法 A 调用方法 B 时,事务应该如何“接力”或“新建”

二、Spring 事务的 7 种传播行为(Propagation)——必背表

传播行为(Propagation)值(枚举)通俗比喻当外层已有事务时当外层无事务时面试最常问场景实际使用频率(2025-2026)
REQUIREDREQUIRED(默认)“有饭局就一起吃,没饭局就自己开一桌”加入外层事务新建事务99% 普通业务方法★★★★★
SUPPORTSSUPPORTS“有饭局就蹭一口,没饭局就不吃”加入外层事务(不开启新事务)不开启事务(以非事务方式执行)查询方法、日志记录★★★☆☆
MANDATORYMANDATORY“必须有饭局才能吃,没饭局就报错”加入外层事务抛出异常必须在已有事务中执行的子操作★★☆☆☆
REQUIRES_NEWREQUIRES_NEW“不管有没有饭局,我都要自己开一桌”挂起外层事务,新建独立事务新建事务独立记账、发红包、发站内信、记录操作日志★★★★☆
NOT_SUPPORTEDNOT_SUPPORTED“有饭局也先别吃,等我吃完再说”挂起外层事务,以非事务方式执行非事务执行需要非事务执行的特殊操作(如导出报表)★★☆☆☆
NEVERNEVER“坚决不许有饭局,吃就报错”抛出异常非事务执行极少用,强制要求不能在事务中执行★☆☆☆☆
NESTEDNESTED“在别人饭局里再开一个小灶”如果外层有事务,则开启一个嵌套事务(保存点)
外层回滚 → 内层也回滚
新建事务保存点场景(部分失败仍可提交部分结果)★★★☆☆

三、最常考、最常踩的 6 种传播行为场景(带代码示例)

@Service
public class OrderService {

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private UserService userService;

    @Autowired
    private LogService logService;

    /**
     * 场景1:默认 REQUIRED(最常见)
     * 下单 → 扣库存 → 扣余额 → 记录日志
     */
    @Transactional
    public void createOrder() {
        orderDao.insertOrder();
        userService.deductBalance();      // 加入外层事务
        logService.writeLog();            // 加入外层事务
    }
}
@Service
public class UserService {

    /**
     * 场景2:REQUIRES_NEW(最常用于独立记录)
     * 即使外层订单事务回滚,发站内信、写操作日志也要成功
     */
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void sendNotification() {
        // 发站内信、写操作日志
        // 即使外层回滚,这里也会提交
    }
}
@Service
public class LogService {

    /**
     * 场景3:NESTED(保存点)
     * 外层事务中有一部分操作允许失败,但整体仍想提交
     */
    @Transactional(propagation = Propagation.NESTED)
    public void recordPartialLog() {
        // 如果这里抛异常,只回滚本方法
        // 外层事务仍可继续提交已执行部分
    }
}

四、事务失效的 8 大经典场景(生产必踩,面试必考)

  1. 非 public 方法(最常见失效原因)
  • @Transactional 只能标注 public 方法
  1. 内部调用绕过代理(最阴险)
   @Transactional
   public void outer() {
       this.inner();   // this 调用,绕过代理 → 事务失效
   }

   public void inner() { ... }

解决:用 AopContext.currentProxy() 或注入自己

  1. 事务方法被 private / final / static 修饰
  2. 数据库不支持事务(MyISAM 引擎)
  3. 事务传播行为为 SUPPORTS / NOT_SUPPORTED / NEVER
  4. 未开启事务管理(@EnableTransactionManagement)
  5. 异常被捕获未抛出(默认只回滚 RuntimeException)
   try {
       // 业务代码
   } catch (Exception e) {
       // 吃了异常 → 事务不回滚
   }

解决:手动 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

  1. 多数据源未正确配置事务管理器

五、面试/生产最常问的 5 个问题(建议背熟)

  1. REQUIRED 和 REQUIRES_NEW 的区别?什么场景用 REQUIRES_NEW?
  2. NESTED 和 REQUIRES_NEW 的区别?(保存点 vs 独立事务)
  3. Spring 事务如何实现?(AOP 动态代理 + TransactionInterceptor)
  4. 事务失效的常见原因有哪些?
  5. 同一个类中,@Transactional 方法调用另一个 @Transactional 方法,事务如何传播?

六、一句话总结

Spring 事务的核心就是:用 AOP 在方法前后插入 begin/commit/rollback 逻辑,而传播行为决定了“当方法嵌套调用时,事务应该如何接力或独立”。

生产口诀
默认 REQUIRED,日志/通知用 REQUIRES_NEW,保存点用 NESTED,查询用 SUPPORTS。

如果你现在能:

  1. 说出 7 种传播行为的含义和典型场景
  2. 写出 3 种事务失效的代码示例
  3. 解释为什么内部调用 this.method() 会导致事务失效

那这篇内容你就已经掌握了 Spring 事务的 80% 精髓。

想继续深入哪一块?
比如:

  • 多数据源分布式事务(Seata / XA)
  • 声明式事务源码(TransactionInterceptor)
  • 编程式事务(TransactionTemplate)
  • @Transactional 所有属性详解(rollbackFor、isolation、timeout 等)

随时告诉我,我继续展开~

文章已创建 3958

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部