前言 在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 { 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 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 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()); } }
首先我们会将事务设置为已完成,以防止重复提交/回滚,与前面rollback方法中的判断相呼应。
如果当前事务创建了一个新的同步,则清空事务同步管理器中的内容(除了当前线程绑定的connectionHolder)
如果当前事务是一个新建的事务,则进行事务完成后的清理
解绑线程的connectionHolder
重置连接的自动提交状态
重置连接的隔离级别
释放连接
恢复挂起事务
绑定挂起事务的connectionHolder
恢复挂起的TransactionSynchronization
进行事务同步管理器的各项设置
激活同步