结合Spring事务源码分析TransactionSynchronizationAdapter之afterCommit回调方法事务回滚问题

5,545 阅读16分钟

问题提出

   在项目开发中我们经常会在事务方法A处理一些与业务关联性较低的逻辑C,对于逻辑C,通常会加入队列或者利用Spring事务同步回调机制去处理。用Spring事务同步回调机制可以保证在业务主逻辑执行成功并提交后,再去执行其他关联逻辑,如发送钉钉消息到业务方或者通知其他业务模块做相应逻辑等。但是假如逻辑C也是事务方法,那么C方法执行过程中抛了异常,A和C的事务会回滚吗?如果事务C传播行为发生改变,C事务抛出异常,那么A和C事务又是如何呢?这个值得探讨,本文主要围绕以下两个问题进行探讨。

@Override
@Transactional(propagation = Propagation.REQUIRED)
public Result A() {
    // 业务逻辑
    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
            // C方法是另一个事务方法,和A()方法不在同一个类中
            C();
        }
    });
    return Result.success(null);
}

问题1:

   回调方法中如果存在事务方法C(默认传播行为),那么C如果报错,会回滚A吗,会回滚C报错前的更新吗?

问题2:

   默认事务传播行为是REQUIRED,假如是传播行为是REQUIRED_NEW,C事务回滚是如何的?

结论

   在讨论这个问题前,先把结论放在问题后面。后面会结合Spring源码分析这些问题。

1)C事务是否存在异常不影响A事务的正常提交,事务C抛出异常后,在默认隔离级别下,异常之前的更新操作不会回滚。

2)传播行为对事务C回滚策略有所影响,当C事务抛出异常,默认传播行为下,C事务方法不回滚;当传播行为为REQUIRES_NEW时,C事务方法会执行回滚策略,这主要与DefaultTransactionStatus中的newTransaction值有关

代码演示

演示代码稍后会贴在github上。

@Override
@Transactional
public Result verify(String orderNo,  Integer value, String checkName) {
    OrderDTO order = orderService.findByBizCode(orderNo);
    order.setValue(value);
    order.updateValue(orderNo, value);

    TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
        @Override
        public void afterCommit() {
            checkService.updateCheckName(orderNo, checkName);
        }
    });
    return Result.success(null);
}
@Override
@Transactional
public void updateCheckName(String orderNo, String checkName) {
    getDao().updateCheckName(orderNo, checkName);
    throw new RuntimeException("测试事务");
}
@Test
public void verify() {
    auctionBizService.verify("A20190614152128000116", 7000009, "tiankunlun88");
}

验证结果:

1)回调方法中updateCheckName(..)是否报错,不影响verify(..)方法提交;

2)对于updateCheckName(..)方法,执行过程中抛出异常时,当propagation = Propagation.Propagation.REQUIRES_NEW时,事务会回滚;当propagation = Propagation.Propagation.REQUIRED时,事务不会回滚。

源码简析

spring在扫描配置文件创建BeanDefination时会解析类中注解作为其属性,在后续实例及初始化过程中,Spring会根据是否存在@Transaction注解,利用后置处理器BeanPostProcessor进行代理对象的创建。当执行类中某一方法时首先调用org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept方法,最终判断是否由代理对象执行方法。关于spring事务网上由很多资料,这里我就本次解决问题,稍微讲一下事务源码。

DynamicAdvisedInterceptor#intercept

@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    Class<?> targetClass = null;
    Object target = null;
    try {
        if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        // May be null. Get as late as possible to minimize the time we
        // "own" the target, in case it comes from a pool...
        target = getTarget();
        if (target != null) {
            targetClass = target.getClass();
        }
        // 获取执行器链
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        // 判断切面是否为空或方法是否是public方法,如果为空或不是public方法就直接反射调用方法即可
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            // We can skip creating a MethodInvocation: just invoke the target directly.
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = methodProxy.invoke(target, argsToUse);
        }
        else {
            // 创建method invocation,然后进行事务增强
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        retVal = processReturnType(proxy, target, method, retVal);
        return retVal;
    }
    finally {
        if (target != null) {
            releaseTarget(target);
        }
        if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

分析: 通过intercept方法,最终调用ReflectiveMethodInvocation的proceed方法,最终通过TransactionAspectSupport#invokeWithinTransaction,执行事务方法,这也是Spring事务源码中最核心的方法之一了。下面我们还看下该方法都做了什么事情。

TransactionAspectSupport#invokeWithinTransaction

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
        throws Throwable {
    //通过事务属性源TransactionAttributeSource读取事务的属性配置,即调用上面名称匹配  
    //事务属性源NameMatchTransactionAttributeSource的方法   
    final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
    
    //获取Spring事务管理IoC容器配置的事务处理器 
    final PlatformTransactionManager tm = determineTransactionManager(txAttr);
    
    //获取目标类指定方法的事务连接点 
    final String joinpointIdentification = methodIdentification(method, targetClass);
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        // 创建事务,将当前事务状态和信息保存到TransactionInfo对象中 
        // 这个方法很很重要,设置传播行为、隔离级别、手动提交、开启事务,另外一点非常重要
        // 绑定事务txInfo到当前线程,这里面用了使用许多缓存和threadLocal对象,方便获取连接信息等
        // 重要一点,设置newTransaction标识位!!!!!!!!!!
        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) {
            //在调用拦截器拦过程中出现异常,则根据事务配置进行提交或回滚处理  
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            //清除与当前线程绑定的事务信息  
            cleanupTransactionInfo(txInfo);
        }
        // 这个方法用来提交事务,回调方法也是在这里触发的。
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
    else {
        //It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        ...................... 略去
    }
}

分析: 这个方法是Spring事务处理核心方法,我们简要介绍下。

  • createTransactionIfNecessary方法创建了TransactionInfo对象,设置传播行为、隔离级别、手动提交(autoCommit=false),绑定事务信息到当前线程中。我们本次要分析的重点对象newTransaction标识位就在这个方法中设置值的。
  • proceedWithInvocation方法,主要执行目标对象方法。
  • completeTransactionAfterThrowing,见名知意,在目标对象方法出现异常后,判断是否进行回滚处理。
  • cleanupTransactionInfo方法主要清除当前线程绑定的事务信息。
  • commitTransactionAfterReturning,这个方法主要用来提交事务。

言归正传,我们还是继续分析newTransaction是如何设置以及它如何影响事务提交和回滚的。

设置事务A newTransaction值

TransactionAspectSupport#createTransactionIfNecessary

protected TransactionInfo createTransactionIfNecessary(
        	PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
        
    // If no name specified, apply method identification as transaction name.
    if (txAttr != null && txAttr.getName() == null) {
        txAttr = new DelegatingTransactionAttribute(txAttr) {
    	  @Override
    	  public String getName() {
    	     return joinpointIdentification;
    	  }
        };
    }
    TransactionStatus status = null;
    if (txAttr != null) {
    	if (tm != null) {
    	        // 获取事务状态,newTransaction就在这个方法中设置的。
    		status = tm.getTransaction(txAttr);
    	}
    	else {
    	   if (logger.isDebugEnabled()) {
    		logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +"] because no transaction manager has been configured");
    	   }
    	}
    }
    return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}

AbstractPlatformTransactionManager#getTransaction

@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
    Object transaction = doGetTransaction();

    // Cache debug flag to avoid repeated checks.
    boolean debugEnabled = logger.isDebugEnabled();

    if (definition == null) {
        // 如果没有配置事务属性,则使用默认的事务属性
        definition = new DefaultTransactionDefinition();
    }
    
    // 检查当前线程是否存在事务,如果已存在事务,那么需要根据在事务属性中定义的事务传播属性来处理事务的产生
    if (isExistingTransaction(transaction)) {
         // 所以afterCommit方法中的事务C方法就是通过这个方法获取TransactionStatus,
            且newTransaction标识位就是在这里设置。
        return handleExistingTransaction(definition, transaction, debugEnabled);
    }

    //检查事务属性中timeout超时属性设置是否合理
    if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
        throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
    }

    // 以下代码就是对事务传播行进行处理
    // 如果事务传播特性配置的是mandatory,当前没有事务存在,抛出异常
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
        throw new IllegalTransactionStateException(
                "No existing transaction found for transaction marked with propagation 'mandatory'");
    }
    // PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW,PROPAGATION_NESTED的逻辑如下
    else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
            definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        SuspendedResourcesHolder suspendedResources = suspend(null);
        if (debugEnabled) {
            logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
        }
        try {
            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
            DefaultTransactionStatus status = newTransactionStatus(
                    definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
            // 开启事务,开启手动提交、设置超时时间、绑定资源到当前线程
            doBegin(transaction, definition);
            
            // 初始化和同步事务状态
            prepareSynchronization(status, definition);
            return status;
        }
        catch (RuntimeException ex) {
            
            // 恢复上一个事务资源
            resume(null, suspendedResources);
            throw ex;
        }
        catch (Error err) {
            resume(null, suspendedResources);
            throw err;
        }
    }
    else {
        // Create "empty" transaction: no actual transaction, but potentially synchronization.
        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
            logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                    "isolation level will effectively be ignored: " + definition);
        }
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        
        // 走到这步newTransaction标志为true,也就是说当事务A中newTransaction标志为truereturn prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
    }
}

分析: getTransaction方法根据isExistingTransaction(transaction)判断当前线是否存在事务不会,如果有事务,走handleExistingTransaction方法。如果没有,会开启事务(即mysql中的begin),设置autoCommit = false,设置事务隔离级别,绑定session到到当前线程等,最终创建DefaultTransactionStatus,这个过程会设置newTransaction值。通过源码可以发现,当当前线程不存在其他事务时,只要事务A的隔离级别不为PROPAGATION_MANDATORY,newTransaction都是为true的。所以事务A的newTransaction标志为true。

事务A提交流程分析

当retVal = invocation.proceedWithInvocation()执行完updateValue后,没有异常,执行finally方法cleanupTransactionInfo(txInfo)后,进行事务提交操作,即执行commitTransactionAfterReturning(txInfo)方法。

TransactionAspectSupport#invokeWithinTransaction

-> invocation.proceedWithInvocation()

-> commitTransactionAfterReturning(txInfo)

  -> 提交事务AbstractPlatformTransactionManager#processCommit

    -> DataSourceTransactionManager#doCommit

      -> 执行con.commit(); 这时候事务A更新操作持久化了

    -> 执行AbstractPlatformTransactionManager#triggerAfterCommit       -> TransactionSynchronizationUtils#invokeAfterCommit,执行回调

小结: 执行invokeAfterCommit后,进入updateCheckName,此时,事务A已经提交了。回答了问题1中:的部分问题C如果报错,会回滚A吗?Spring执行afterCommit中的回调方法,不管C事务是否存在异常,都不是影响事务A,因为A此时已经commit持久化了!!!

事务C提交流程分析

  执行流程与事务A执行流程大都相似,但获取TransactionStatus方法有所不同,在事务C执行流程抛出了异常,这时事务又是如何回滚的呢,这部分内容我们会得到问题2的答案。

设置事务C newTransaction值

AbstractPlatformTransactionManager#getTransaction

@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
    Object transaction = doGetTransaction();

    // Cache debug flag to avoid repeated checks.
    boolean debugEnabled = logger.isDebugEnabled();

    if (definition == null) {
        // Use defaults if no transaction definition given.
        definition = new DefaultTransactionDefinition();
    }
    
    // 这时后线程存在事务,走handleExistingTransaction方法,而事务C的newTransaction
    // 标志位就在这个方法中设置。
    if (isExistingTransaction(transaction)) {
        // Existing transaction found -> check propagation behavior to find out how to behave.
        return handleExistingTransaction(definition, transaction, debugEnabled);
    }
    ........
}

分析: 此时当前线程存在事务,执行handleExistingTransaction方法。接下来我们来看下handleExistingTransaction源码。

AbstractPlatformTransactionManager#handleExistingTransaction

private TransactionStatus handleExistingTransaction(
        TransactionDefinition definition, Object transaction, boolean debugEnabled)
        throws TransactionException {
    // 如果事务传播特性为:never,则抛出异常
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
        throw new IllegalTransactionStateException(
                "Existing transaction found for transaction marked with propagation 'never'");
    }
    
    // 如果事务传播特性是PROPAGATION_NOT_SUPPORTED
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
        if (debugEnabled) {
            logger.debug("Suspending current transaction");
        }
        // 当前线程存在事务,则将事务挂起
        Object suspendedResources = suspend(transaction);
        boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
        // 创建非事务的事务状态,让方法不走事务
        return prepareTransactionStatus(
                definition, null, false, newSynchronization, debugEnabled, suspendedResources);
    }
    
    // 重点来来,如果传播行为PROPAGATION_REQUIRES_NEW
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
        if (debugEnabled) {
            logger.debug("Suspending current transaction, creating new transaction with name [" +
                    definition.getName() + "]");
        }
        SuspendedResourcesHolder suspendedResources = suspend(transaction);
        try {
            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
            // 设置newnewTransaction位true
            DefaultTransactionStatus status = newTransactionStatus(
                    definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
            doBegin(transaction, definition);
            prepareSynchronization(status, definition);
            return status;
        }
        catch (RuntimeException beginEx) {
            resumeAfterBeginException(transaction, suspendedResources, beginEx);
            throw beginEx;
        }
        catch (Error beginErr) {
            resumeAfterBeginException(transaction, suspendedResources, beginErr);
            throw beginErr;
        }
    }
    
    // 这一段主要是分析嵌套事务的,我们基本不用。
    if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
        //如果不允许事务嵌套,则抛出异常
        if (!isNestedTransactionAllowed()) {
            throw new NestedTransactionNotSupportedException(
                    "Transaction manager does not allow nested transactions by default - " +
                    "specify 'nestedTransactionAllowed' property with value 'true'");
        }
        if (debugEnabled) {
            logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
        }
        
        // 如果允许使用savepoint保存点保存嵌套事务,为当前事务创建一个保存点
        if (useSavepointForNestedTransaction()) {
            // Create savepoint within existing Spring-managed transaction,
            // through the SavepointManager API implemented by TransactionStatus.
            // Usually uses JDBC 3.0 savepoints. Never activates Spring synchronization.
            DefaultTransactionStatus status =
                    prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
            status.createAndHoldSavepoint();
            return status;
        }
        else {
            // Nested transaction through nested begin and commit/rollback calls.
            // Usually only for JTA: Spring synchronization might get activated here
            // in case of a pre-existing JTA transaction.
            boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
            DefaultTransactionStatus status = newTransactionStatus(
                    definition, transaction, true, newSynchronization, debugEnabled, null);
            doBegin(transaction, definition);
            prepareSynchronization(status, definition);
            return status;
        }
    }
    
    // Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
    if (debugEnabled) {
        logger.debug("Participating in existing transaction");
    }
    
    // 下面这个if主要是判断隔离级别和数据库默认隔离级别是否一致,如果不一致,判断当前的隔离级别和definition的隔离级别
    // 是否一致,不一致就抛异常。这段代码主要是隔离级别的参数校验。
    if (isValidateExistingTransaction()) {
        if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
            Integer currentIsolationLevel = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
            if (currentIsolationLevel == null || currentIsolationLevel != definition.getIsolationLevel()) {
                Constants isoConstants = DefaultTransactionDefinition.constants;
                throw new IllegalTransactionStateException("Participating transaction with definition [" +
                        definition + "] specifies isolation level which is incompatible with existing transaction: " +
                        (currentIsolationLevel != null ?
                                isoConstants.toCode(currentIsolationLevel, DefaultTransactionDefinition.PREFIX_ISOLATION) :
                                "(unknown)"));
            }
        }
        if (!definition.isReadOnly()) {
            if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                throw new IllegalTransactionStateException("Participating transaction with definition [" +
                        definition + "] is not marked as read-only but existing transaction is");
            }
        }
    }
    boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
    
    // 剩下的就是PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED,从这里可以看出newTransaction为false
    return prepareTransactionStatus(definition, transaction, false, newSynchronization, debugEnabled, null);
}

小结: 这个方法主要是根据事务传播属性,判断是否要重新创建事务,还是使用原来的事务。如果是默认传播行为,还是使用原来的事务,此时newTranaction值为false,在这里确定了事务C的newTranaction值为false。 如果是传播行为REQUIRES_NEW,则会新建事务,newTranaction即为true。

newTranaction如何影响事务C提交

在事务事务C抛出异常后,事务C是否进行会回滚呢,可能我们感官上以为出现了异常,应该会回滚的,但是事实可能并非我们想象得那般。分了分析问题2,我们需要再次贴上事务执行的部分源代码,方便理解。 TransactionAspectSupport#invokeWithinTransaction

protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
        throws Throwable {
    // 略......
    if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
        TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
        Object retVal = null;
        try {
            retVal = invocation.proceedWithInvocation();
        }
        catch (Throwable ex) {
            //在调用拦截器拦过程中出现异常,则根据事务配置进行提交或回滚处理  
            completeTransactionAfterThrowing(txInfo, ex);
            throw ex;
        }
        finally {
            //清除与当前线程绑定的事务信息  
            cleanupTransactionInfo(txInfo);
        }
        
        // 这个方法用来提交事务
        commitTransactionAfterReturning(txInfo);
        return retVal;
    }
    else {
        // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
        ...................... 略去
    }
}

当invocation.proceedWithInvocation()方法报错,即updateCheckName抛异常时,进入completeTransactionAfterThrowing方法,完成事务操作。 TransactionAspectSupport#completeTransactionAfterThrowing

protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
    if (txInfo != null && txInfo.hasTransaction()) {
        if (logger.isTraceEnabled()) {
            logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
                    "] after exception: " + ex);
        }
        
        // 满足配置的异常回滚策略,会进入回滚方法rollback
        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;
            }
        }
    }
}

分析: 从completeTransactionAfterThrowing方法中可以看出,并不是所有的异常都会使事务在出现异常时回滚。对于不支持回滚的异常,Spring依然会提交事务。 AbstractPlatformTransactionManager#processRollback

private void processRollback(DefaultTransactionStatus status) {
    try {
        try {
            // 触发完成前的回调操作
            triggerBeforeCompletion(status);
            //嵌套事务回滚处理
            if (status.hasSavepoint()) {
                if (status.isDebug()) {
                    logger.debug("Rolling back transaction to savepoint");
                }
                //回滚挂起在保存点的事务
                status.rollbackToHeldSavepoint();
            }
            
            // 当前事务中新事务的回滚操作,就是根据newTransaction标志位进行判断的
            // 所以在这里事务C方法是默认传播行为时不会进行回滚操作,当传播行为为required_new
            // 时,会进入doRollback方法回滚之前的操作。
            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);
    }
}

分析: 当执行processRollback方法时,会判断事务C是否是新事务,有前面当分析我们已经得知newTransaction为false。因此,不会执行doRollback,也就是说事务C不会执行回滚操作!

此时,回答了问题1:事务C异常,会回滚C报错前的更新吗? 但是由引来了一个问题,事务C的数据是何时持久化的呢?接下来,我们分析下,在默认隔离级别下,事务C数据在何时进行持久化的。

事务C持久化分析

当事务C抛出异常后,意味着事务A执行执行triggerAfterCommit失败,进入finally流程,执行cleanupAfterCompletion(status)方法,也就是在这方法中事务C的数据落地了。 DataSourceTransactionManager#cleanupAfterCompletion

private void cleanupAfterCompletion(DefaultTransactionStatus status) {
    status.setCompleted();
    if (status.isNewSynchronization()) {
    	TransactionSynchronizationManager.clear();
    }
    // 此时回到事务A的执行流程中,isNewTransaction为true
    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());
    }
}

DataSourceTransactionManager#doCleanupAfterCompletion

@Override
protected void doCleanupAfterCompletion(Object transaction) {
    DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;

    // Remove the connection holder from the thread, if exposed.
    if (txObject.isNewConnectionHolder()) {
        //从TransactionSynchronizationManager中解绑相应的connectionHolder
        TransactionSynchronizationManager.unbindResource(this.dataSource);
    }

    //还原Connection
    Connection con = txObject.getConnectionHolder().getConnection();
    try {
        if (txObject.isMustRestoreAutoCommit()) {
            // 当这段代码执行后,数据落地了。
            con.setAutoCommit(true);
        }
        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");
        }
        //如果是newConnection将这个链接关闭,如果是连接池将还给连接池
        DataSourceUtils.releaseConnection(con, this.dataSource);
    }
    // 设置transactionActive为false
    txObject.getConnectionHolder().clear();
}

分析: 将将连接autoCommit设置为true时,事务C的数据就落地了。

传播行为对事务C回滚的影响分析

  事务C的传播行为为PROPAGATION_REQUIRES_NEW时,newTransaction值为true,在抛出异常后执行doRollback方法,回滚数据。 此时回答了问题2,也就是说当传播行为不同时,事务C回滚策略不一样。

AbstractPlatformTransactionManager#processRollback

private void processRollback(DefaultTransactionStatus status) {
    try {
        try {
            // 触发完成前的回调操作
            triggerBeforeCompletion(status);
            //嵌套事务回滚处理
            if (status.hasSavepoint()) {
                if (status.isDebug()) {
                    logger.debug("Rolling back transaction to savepoint");
                }
                //回滚挂起在保存点的事务
                status.rollbackToHeldSavepoint();
            }
            
            // 当前事务中新事务的回滚操作,就是根据newTransaction标志位进行判断的
            // 所以在这里事务C方法是默认传播行为时不会进行回滚操作,当传播行为为required_new
            // 时,会进入doRollback方法回滚之前的操作。
            else if (status.isNewTransaction()) {
                if (status.isDebug()) {
                    logger.debug("Initiating transaction rollback");
                }
                //回滚处理,由具体的事务处理器实现
                doRollback(status);
            }
         ........
    }
    finally {
        cleanupAfterCompletion(status);
    }
}

DataSourceTransactionManager#doRollback

@Override
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);
    }
}

举一反三

如果事务C没有抛出异常,事务C的提交会在哪里呢?

   如果是默认传播行为,事务C没有异常,它的数据仍然是在事务A提交事务后才进行落地的,也就是doCleanupAfterCompletion方法中设置autoCommit=true时。但是当传播行为为REQUIRES_NEW时,就不一样了,事务C的提交,在自己的事务中进行。

总结

1.Spring事务同步回调afterCommit方法中的事务方法C抛异常,不影响主方法A正常提交;

2.不同传播行为对事务C回滚影响不同,默认传播行为时,C事务方法不回滚。当传播行为REQUIRES_NEW时,C事务方法在异常是,会执行回滚策略,这主要与DefaultTransactionStatus中的newTransaction值有关;

3.事务A方法执行DataSourceTransactionManager#doCleanupAfterCompletionautoComumit设置为true时,C事务中的数据就会被持久化。当然如果是隔离级别发生变化,落地时机也会发生改变,这主要与事务C的传播行为有关,本质上还是与newTransaction有关。

4.如果要保证afterCommit中的事务方法有效,需要将传播行为设置为Required_New,这一点Spring也有过建议。