Spring事务源码(三)
本文主要讲述Spring事务在获得事务对象后,再执行目标对象的原生方法后遇到异常后,如何处理事务。通常是通过completeTransactionAfterThrowing这个方法解决
文章目录
前言
我们需要回到TransactionAspectSupport#invokeWithinTransaction这个方法来,前两篇文章都是讲诉如何获得TransactionStatus,最后封装成TransactionInfo。 而本文是讲述获得事务后,执行完目标方法后catch到异常后该怎么处理。 这里主要解析completeTransactionAfterThrowing
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
// 捕获异常后如何处理
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
//清除当前方法上的事务信息
cleanupTransactionInfo(txInfo);
}
// 如果执行到这里说明没有异常,尝试提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
一、completeTransactionAfterThrowing
执行目标方法后该怎么处理事务
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) {
throw ex2;
}
catch (RuntimeException ex2) {
throw ex2;
}
catch (Error err) {
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;
}
}
}
}
transactionAttribute的rollbackOn方法
这个方法判断Spring事务针对此异常是回滚还是该提交,具体实现类的方式是RuleBasedTransactionAttribute#rollbackOn
@Override
public boolean rollbackOn(Throwable ex) {
RollbackRuleAttribute winner = null;
int deepest = Integer.MAX_VALUE;
if (this.rollbackRules != null) {
// 循环遍历rollbackRules,真实是先遍历RollbackRuleAttribute,然后在遍历NoRollbackRuleAttribute
// 总之就是要找出符合要求(ex.getName().contains(rule.exceptionName))
// 且层级次数最低的那个Rule
for (RollbackRuleAttribute rule : this.rollbackRules) {
int depth = rule.getDepth(ex);
if (depth >= 0 && depth < deepest) {
deepest = depth;
winner = rule;
}
}
}
// User superclass behavior (rollback on unchecked) if no rule matches.
// 如果没有找着符合要求的Rule,则调用父类的rollbackOn判断
// 判断当前ex是RuntimeException 或是Error
if (winner == null) {
logger.trace("No relevant rollback rule found: applying default rules");
// return (ex instanceof RuntimeException || ex instanceof Error);
return super.rollbackOn(ex);
}
// 如果有找到合适的Rule
// 则判断是RollbackRuleAttribute 则需要回滚是否
// 是NoRollbackRuleAttribute 则不需要回滚
return !(winner instanceof NoRollbackRuleAttribute);
}
// RollbackRuleAttribute的方法: 获取支持的异常以及层级次数
// depth默认是0, 如果RollbackRuleAttribute的exceptionClass包含此异常,直接返回depth
// 如果是Throwable返回-1
// 否则拿exceptionClass的父类递归查找
private int getDepth(Class<?> exceptionClass, int depth) {
if (exceptionClass.getName().contains(this.exceptionName)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (exceptionClass == Throwable.class) {
return -1;
}
return getDepth(exceptionClass.getSuperclass(), depth + 1);
}
二、PlatformTransactionManager的rollback
如果在上一步transactionAttribute.rollbackOn(ex)判断需要回滚事务,则调用事务管理器的rollback方法来回滚事务
@Override
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);
}
private void processRollback(DefaultTransactionStatus status) {
try {
try {
// 主要是循环遍历事务同步管理器的synchronization.beforeCompletion
// 当前还得判断是当前方法里是开启了新事务
triggerBeforeCompletion(status);
// 如果是通过传播方式Nested,加入了savePoint,则回滚到创建savePoint那里
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");
}
// 从status.getTransaction()取出事务对象,再取出connection,通过调用connection.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;
}
// 循环遍历事务同步管理器的事务同步器
// 主要是循环遍历事务同步管理器的synchronization.afterCompletion
// 当前还得判断是当前方法里是开启了新事务
// 并且清空事务同步器
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
}
finally {
cleanupAfterCompletion(status);
}
}
三、cleanupAfterCompletion
处理完回滚事务逻辑后(不一定会回滚,只有在开始事务的方法上才会真正回滚),持有的datasource的Connection设置还原且释放, 如果挂起了事务,需要恢复原有事务状态。
private void cleanupAfterCompletion(DefaultTransactionStatus status) {
//标记当前事务status对象已经完完成了,后续就不要来处理它了
status.setCompleted();
if (status.isNewSynchronization()) {
// 如果是开始事务的status,则还原TransactionSynchronizationManager
TransactionSynchronizationManager.clear();
}
if (status.isNewTransaction()) {
// 如果是开始事务的status,则还原事务相关的的一些属性
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());
}
}
doCleanupAfterCompletion
主要是还原datasource的connection的一些设置如autoCommit, 隔离级别,同时与当前线程解绑不在持有connection,并释放connection
@Override
protected void doCleanupAfterCompletion(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
// Remove the connection holder from the thread, if exposed.
if (txObject.isNewConnectionHolder()) {
// 从线程变量中删除Connection,不再持有Connection
TransactionSynchronizationManager.unbindResource(this.dataSource);
}
// Reset connection.
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 把Connection对象还原成AutoCommit
if (txObject.isMustRestoreAutoCommit()) {
con.setAutoCommit(true);
}
// 还原dataSource的隔离级别
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (Throwable ex) {
logger.debug("Could not reset JDBC Connection after transaction", ex);
}
if (txObject.isNewConnectionHolder()) {
if (logger.isDebugEnabled()) {
logger.debug("Releasing JDBC Connection [" + con + "] after transaction");
}
//释放Connection
DataSourceUtils.releaseConnection(con, this.dataSource);
}
// 还原ConnectionHolder的属性值
txObject.getConnectionHolder().clear();
}
resume
如果当前事务状态对象曾挂起过之前的事务对象,需要恢复之前的事务对象
protected final void resume(Object transaction, SuspendedResourcesHolder resourcesHolder)
throws TransactionException {
if (resourcesHolder != null) {
Object suspendedResources = resourcesHolder.suspendedResources;
if (suspendedResources != null) {
// 将之前的connection对象重新绑定当前线程变量
doResume(transaction, suspendedResources);
}
// 如果还保存了之前的事务同步管理器里的东西,则相应的还原
List<TransactionSynchronization> suspendedSynchronizations = resourcesHolder.suspendedSynchronizations;
if (suspendedSynchronizations != null) {
TransactionSynchronizationManager.setActualTransactionActive(resourcesHolder.wasActive);
TransactionSynchronizationManager.setCurrentTransactionIsolationLevel(resourcesHolder.isolationLevel);
TransactionSynchronizationManager.setCurrentTransactionReadOnly(resourcesHolder.readOnly);
TransactionSynchronizationManager.setCurrentTransactionName(resourcesHolder.name);
// 调用下这些事务同步器的resume方法,并把这些事务同步器重新放入事务同步管理器的线程变量中
doResumeSynchronization(suspendedSynchronizations);
}
}
}
总结
在执行目标方法后遇到异常的处理流程:
- 根据目标方法的事务属性针对此异常是否需要回滚事务,还是提交事务
- 回滚事务的话,调用事务管理器PlatformTransactionManager的rollback进行回滚逻辑
- 回滚逻辑:
- 如果是新事务,遍历调用事务同步器的beforeCompletion的方法
- 如果当时事务是内嵌事务,则调用status的rollbackToHeldSavepoint方法,回滚savePoint
- 如果当前事务状态transactionStatus对象是开启过事务的,则取出Connection调用它的rollback方法,进行真正的回滚事务的动作
- 如果当前事务状态transactionStatus对象未开启过事务,但判断出它有事务,则标签当前线程上的事务需要回滚,交予开始事务的事务状态对象transactionStatus来进行回滚事务
- 然后判断如果是开始事务的事务状态对象,则调用事务同步器的invokeAfterCompletion方法
- 最后调用事务管理器的cleanupAfterCompletion方法,主要是判断如果当前开启过事务,然后就还原事务同步器,还原dataSource的Connection对象的一些属性,如隔离级别,autoCommit,然后从当前线程变量中释放掉Connection。如果当前事务状态对象还持有了挂起之前的事务资源对象,则进行之前的事务状态恢复。