以下代码来自org.springframework.transaction.interceptor.RuleBasedTransactionAttribute#rollbackOn
            
            
              java
              
              
            
          
          	/**
	 * Winning rule is the shallowest rule (that is, the closest in the
	 * inheritance hierarchy to the exception). If no rule applies (-1),
	 * return false.
	 * @see TransactionAttribute#rollbackOn(java.lang.Throwable)
	 */
	@Override
	public boolean rollbackOn(Throwable ex) {
		if (logger.isTraceEnabled()) {
			logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
		}
		RollbackRuleAttribute winner = null;
		int deepest = Integer.MAX_VALUE;
		if (this.rollbackRules != null) {
			for (RollbackRuleAttribute rule : this.rollbackRules) {
				int depth = rule.getDepth(ex);
				if (depth >= 0 && depth < deepest) {
					deepest = depth;
					winner = rule;
				}
			}
		}
		if (logger.isTraceEnabled()) {
			logger.trace("Winning rollback rule is: " + winner);
		}
		// User superclass behavior (rollback on unchecked) if no rule matches.
		if (winner == null) {
			logger.trace("No relevant rollback rule found: applying default rules");
			return super.rollbackOn(ex);
		}
		return !(winner instanceof NoRollbackRuleAttribute);
	}这段代码是 Spring 事务框架中 RollbackRuleAttribute 类的 rollbackOn(Throwable ex) 方法,它的作用是:判断当抛出某个异常时,是否应该触发事务回滚。
我们来逐行深入解析其逻辑、设计思想和关键点。
🎯 一、核心目标
✅ 这个方法是做什么的?
给定一个异常(Throwable ex),检查当前配置的"回滚规则"(rollbackRules),决定:
这个异常发生时,事务是否应该回滚?
返回值:
- true→ 回滚
- false→ 不回滚
📚 二、背景知识:Spring 的回滚规则
在 @Transactional 注解中,你可以通过以下属性定义回滚行为:
            
            
              java
              
              
            
          
          @Transactional(
    rollbackFor = {SQLException.class},     // 遇到这些异常就回滚
    noRollbackFor = {IOException.class}     // 遇到这些异常不回滚
)Spring 内部会把这些配置转换成两种规则对象:
| 规则类型 | 对应类 | 行为 | 
|---|---|---|
| rollbackFor | RollbackRuleAttribute | 匹配则回滚 | 
| noRollbackFor | NoRollbackRuleAttribute | 匹配则不回滚 | 
这些规则都存放在 this.rollbackRules 列表中。
🔍 三、逐行解析
1. 日志输出(调试用)
            
            
              java
              
              
            
          
          if (logger.isTraceEnabled()) {
    logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
}👉 记录正在判断哪个异常是否要回滚。
2. 初始化变量
            
            
              java
              
              
            
          
          RollbackRuleAttribute winner = null;
int deepest = Integer.MAX_VALUE;- winner:最终胜出的规则(即最匹配的那个)
- deepest:记录当前找到的"最小继承深度",初始设为最大值,表示还没找到
📌 注意:这里"deepest"其实是"离异常最近的继承距离",名字有点反直觉。
3. 遍历所有回滚规则
            
            
              java
              
              
            
          
          if (this.rollbackRules != null) {
    for (RollbackRuleAttribute rule : this.rollbackRules) {
        int depth = rule.getDepth(ex);
        if (depth >= 0 && depth < deepest) {
            deepest = depth;
            winner = rule;
        }
    }
}关键:rule.getDepth(ex)
- 计算该规则对应的异常类型(如 SQLException)与传入异常ex之间的继承层级距离。
- 例如:
- ex是- SQLSyntaxErrorException
- rule是- SQLException.class
- 继承链:SQLSyntaxErrorException → SQLException → Exception
- getDepth()返回- 1(差一级)
 
赢家规则(Winning Rule)判定条件:
"最浅层的规则获胜 " ------ 即 继承距离最小、最具体的异常类型优先。
✅ 例子:
            
            
              java
              
              
            
          
          rollbackFor = {
    Exception.class,      // depth = 2(较远)
    SQLException.class    // depth = 1(更近)
}如果抛出 SQLTimeoutException,则 SQLException.class 规则胜出,因为它更"具体"。
4. 输出胜出规则(日志)
            
            
              java
              
              
            
          
          if (logger.isTraceEnabled()) {
    logger.trace("Winning rollback rule is: " + winner);
}👉 帮助开发者调试:到底是哪个规则起作用了。
5. 如果没有匹配的规则
            
            
              java
              
              
            
          
          if (winner == null) {
    logger.trace("No relevant rollback rule found: applying default rules");
    return super.rollbackOn(ex);
}- 没有规则匹配当前异常(比如你只配置了 rollbackFor=SQLException,但抛出了IOException)。
- 此时走父类默认逻辑:super.rollbackOn(ex)
默认规则是什么?
来自 DefaultTransactionAttribute 或 RuleBasedTransactionAttribute 的父类:
仅对
RuntimeException和Error及其子类自动回滚对
Exception(检查异常)不自动回滚
📌 所以:
- throw new RuntimeException()→ 回滚
- throw new IOException()→ 不回滚(除非显式配置- rollbackFor)
6. 判断胜出规则是否是"不回滚"规则
            
            
              java
              
              
            
          
          return !(winner instanceof NoRollbackRuleAttribute);这是最关键的一步!
| 胜出规则类型 | 返回值 | 是否回滚 | 
|---|---|---|
| RollbackRuleAttribute(如rollbackFor=SQLException) | true | ✅ 回滚 | 
| NoRollbackRuleAttribute(如noRollbackFor=IOException) | false | ❌ 不回滚 | 
📌 也就是说:
- 如果最匹配的规则是 noRollbackFor,那就不回滚
- 否则(是 rollbackFor),就回滚
🎯 四、完整流程图解
开始
  ↓
遍历所有 rollbackRules
  ↓
对每个 rule,计算 rule.getDepth(ex)
  ↓
保留 depth 最小(最具体)的那个 rule 作为 winner
  ↓
是否有 winner?
  ├─ 否 → 使用默认规则:RuntimeException/Error 才回滚
  └─ 是 → 看 winner 是否是 NoRollbackRuleAttribute
           ├─ 是 → 不回滚(return false)
           └─ 否 → 回滚(return true)✅ 五、举个实际例子
            
            
              java
              
              
            
          
          @Transactional(
    rollbackFor = {Exception.class},
    noRollbackFor = {IllegalArgumentException.class}
)
public void doSomething() {
    throw new IllegalArgumentException("参数错误");
}调用 rollbackOn(ex) 时:
- rollbackRules包含两个规则:- RollbackRuleAttribute: Exception.class
- NoRollbackRuleAttribute: IllegalArgumentException.class
 
- getDepth(IllegalArgumentException):- Exception.class→ depth = 1
- IllegalArgumentException.class→ depth = 0(完全匹配!)
 
- winner = NoRollbackRuleAttribute(depth=0 更小)
- return !(winner instanceof NoRollbackRuleAttribute)→- false
- 结果:不回滚
✅ 符合预期:noRollbackFor 优先级更高。
✅ 六、设计精妙之处
| 特性 | 说明 | 
|---|---|
| 最具体优先 | 避免宽泛规则覆盖精确规则 | 
| noRollbackFor 可覆盖 rollbackFor | 提供灵活控制 | 
| 默认回滚策略兜底 | 保证未配置时也有合理行为 | 
| 可扩展 | 可自定义 RollbackRuleAttribute | 
✅ 七、常见问题排查
| 现象 | 可能原因 | 
|---|---|
| @Transactional不回滚 | 抛的是 Exception,没加rollbackFor | 
| 明明写了 noRollbackFor还是回滚了 | 异常类型不匹配,或 winner是其他规则 | 
| 自定义异常不回滚 | 必须继承 RuntimeException或显式配置rollbackFor | 
✅ 总结
这个 rollbackOn 方法是 Spring 事务"回滚决策引擎",它:
- 从多个规则中选出最匹配的一个(基于继承深度)
- 优先尊重 noRollbackFor(即"不回滚"规则胜出就不回滚)
- 没有匹配规则时,使用默认策略 (只对 RuntimeException/Error回滚)
理解它有助于你:
- 正确配置 @Transactional(rollbackFor/noRollbackFor)
- 排查"事务该回滚却没回滚"或"不该回滚却回滚了"的问题
- 深入掌握 Spring 事务的底层机制
🧠 一句话总结:
"谁最像这个异常,谁说了算;如果是 noRollbackFor 赢了,就不回滚。"