目录
  1. 前言
  2. 源码分析
  3. 异常回滚判断
    1. 异常列表匹配
    2. 默认匹配
  4. 回滚事务
  5. 回滚完成后清理
spring事务(5) spring异常回滚源码分析

前言

spring事务入口及整体流程中有讲到spring事务中有一个很重要的模块就是异常回滚。这一次我们来分析异常回滚操作的源码。

源码分析

其是在TransactionAspectSupport类的completeTransactionAfterThrowing方法中实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
else {
// We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true.
try {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by commit exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by commit error", ex);
throw err;
}
}
}
}

如果我们忽略掉异常之类的语句,我们可以发现整段代码非常简短,就只做了这么一件事:

  • 判断当前异常是否应该回滚,若应该回滚,则回滚,否则提交事务

异常回滚判断

首先先来看这么一句if (txInfo.transactionAttribute.rollbackOn(ex)),这一句是用来判断当前异常是否回滚,例如默认情况下RuntimeException会回滚也是在这里定义的。下面是这段的源码,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public boolean rollbackOn(Throwable 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 (winner == null) {
return super.rollbackOn(ex);
}

return !(winner instanceof NoRollbackRuleAttribute);
}

这段代码可以分为两个部分:

  • 对异常列表进行匹配
  • 默认匹配

异常列表匹配

此处有两个疑问:异常列表是什么?其中的异常顺序是怎样的?

对于第一个问题:

异常列表就是一个List,这个List中会存在两个类RollbackRuleAttribute和NoRollbackRuleAttribute,其中NoRollbackRuleAttribute是RollbackRuleAttribute的子类。那么很显然,它们分别对应@Transaction属性中回滚异常和不回滚异常。

对于第二个问题:

我们从spring事务注解的解析过程中可以找到答案,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 该方法在SpringTransactionAnnotationParser类中

protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
rbta.setTimeout(attributes.getNumber("timeout").intValue());
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));
ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();
Class<?>[] rbf = attributes.getClassArray("rollbackFor");
for (Class<?> rbRule : rbf) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
String[] rbfc = attributes.getStringArray("rollbackForClassName");
for (String rbRule : rbfc) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
Class<?>[] nrbf = attributes.getClassArray("noRollbackFor");
for (Class<?> rbRule : nrbf) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
String[] nrbfc = attributes.getStringArray("noRollbackForClassName");
for (String rbRule : nrbfc) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
rbta.getRollbackRules().addAll(rollBackRules);
return rbta;
}

可以在里面看到我们的异常列表中异常的存储顺序是rollbackFor$\rightarrow$rollbackForClassName$\rightarrow$noRollbackFor$\rightarrow$noRollbackForClassName。

知道了这两个问题的答案,我们来研究一下最终到底会匹配哪一个异常。截出刚刚这部分的代码,

1
2
3
4
5
6
7
for (RollbackRuleAttribute rule : this.rollbackRules) {
int depth = rule.getDepth(ex);
if (depth >= 0 && depth < deepest) {
deepest = depth;
winner = rule;
}
}

其中getDepth是用来计算ex到rule的继承高度的。那么优先匹配结果就可以一下子看出来了:

  • 若ex匹配多个异常,则选用继承关系最近的
  • 若继承关系最近的又多个,则选用匹配列表靠前的

默认匹配

默认匹配就只有一句判断,

1
2
3
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}

可以看到默认会在unchecked exception出现的情况下进行回滚操作。

注意:即使我们已经指定了rollbackFor属性,unchecked exception也同样会回滚,除非我们在noRollbackFor中将其排除。

回滚事务

打开回滚事务的源码,

1
2
3
4
5
6
7
8
9
10
11
// 该方法在AbstractPlatformTransactionManager类中

public final void rollback(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}

DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
processRollback(defStatus);
}

我们看到这里有一个status.isCompleted()的判断,这个判断是为了防止事务已经执行过提交/回滚操作了。

那我们接着看processRollback方法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
private void processRollback(DefaultTransactionStatus status) {
try {
try {
triggerBeforeCompletion(status);
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
else if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
}
catch (RuntimeException ex) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw ex;
}
catch (Error err) {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
throw err;
}
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
}
finally {
cleanupAfterCompletion(status);
}
}

对于第一句triggerBeforeCompletion(status),这一句其实是用来进行TransactionSynchronization的beforeCompletion的回调的。(见事务同步管理器)


对于status.hasSavepoint()的情况,一般是用于嵌套事务的情况。利用保存点回滚来实现嵌套事务。

1
2
3
4
5
6
7
public void rollbackToHeldSavepoint() throws TransactionException {
if (!hasSavepoint()) {
throw new TransactionUsageException("No savepoint associated with current transaction");
}
getSavepointManager().rollbackToSavepoint(getSavepoint());
setSavepoint(null);
}
1
2
3
4
5
6
7
8
9
// 最终调用
public void rollbackToSavepoint(Object savepoint) throws TransactionException {
try {
getConnectionHolderForSavepoint().getConnection().rollback((Savepoint) savepoint);
}
catch (Throwable ex) {
throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex);
}
}

可以看到此处利用了rollback(savepoint)这个接口,不过需要注意的是这个特性在JDBC3.0之后才开始支持。


对于status.isNewTransaction()的情况,

1
2
3
4
5
6
7
8
9
10
11
12
13
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}

可以从源码中看到,只是调用了数据库驱动提供的接口rollback()。


对于if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure())的情况,

如果当前事务不是独立的事务,则只能等待事务链执行完成之后再做回滚操作

我也不是特别清楚,以上是来自http://blog.csdn.net/heroqiang/article/details/79057925这篇文章中的解释。不过可以看到里面对当前事务做了一个rollbackOnly标记,也就是说该事务最终还是会回滚,只是不是现在回滚。


在这之后,如果未抛出异常,会执行triggerAfterCompletion,与前面的triggerBeforeCompletion相对应,该方法是用来回调TransactionSynchronization的afterCompletion方法的。


最后执行完成后的清理工作。

回滚完成后清理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
status.setCompleted();
if (status.isNewSynchronization()) {
TransactionSynchronizationManager.clear();
}
if (status.isNewTransaction()) {
doCleanupAfterCompletion(status.getTransaction());
}
if (status.getSuspendedResources() != null) {
if (status.isDebug()) {
logger.debug("Resuming suspended transaction after completion of inner transaction");
}
resume(status.getTransaction(), (SuspendedResourcesHolder) status.getSuspendedResources());
}
}
  1. 首先我们会将事务设置为已完成,以防止重复提交/回滚,与前面rollback方法中的判断相呼应。
  2. 如果当前事务创建了一个新的同步,则清空事务同步管理器中的内容(除了当前线程绑定的connectionHolder)
  3. 如果当前事务是一个新建的事务,则进行事务完成后的清理
    • 解绑线程的connectionHolder
    • 重置连接的自动提交状态
    • 重置连接的隔离级别
    • 释放连接
  4. 恢复挂起事务
    • 绑定挂起事务的connectionHolder
    • 恢复挂起的TransactionSynchronization
    • 进行事务同步管理器的各项设置
    • 激活同步
文章作者: 谷河
文章链接: https://www.lyytaw.com/spring/spring_tx_rollback/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 谷河|BLOG