以下代码来自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是SQLSyntaxErrorExceptionrule是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.classNoRollbackRuleAttribute: IllegalArgumentException.class
getDepth(IllegalArgumentException):Exception.class→ depth = 1IllegalArgumentException.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 赢了,就不回滚。"