Spring事务嵌套

有一段比较有意思的代码。

AService有事务在afterCommit后触发没有事务的BService#exec,在BService#exec中 触发有事务的CService#exec,并且CService#exec也有 afterCommit 事务提交后的操作。(最终CService#exec#afterCommit没有执行)

eg:

@Service
public class AService {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private BService bService;

	@Transactional
	public void exec() {
		logger.info("正在执行AService#exec");
		/**
		 * 执行一些业务逻辑。。。
		 */
		// 事务提交后触发后续逻辑
		if (TransactionSynchronizationManager.isActualTransactionActive()) {
			TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
				@Override
				public void afterCommit() {
					logger.info("AService事务提交后触发BService");
					bService.exec();
				}
			});
		}

	}

}
@Service
public class BService {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private CService cService;

	public void exec() {
		logger.info("正在执行BServiceExec");
		/**
		 * 执行一些业务逻辑。。。
		 */
		cService.exec();
	}

}
@Service
public class CService {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	@Transactional
	public void exec() {
		logger.info("正在执行 CService#exec");
		if (TransactionSynchronizationManager.isActualTransactionActive()) {
			TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
				@Override
				public void afterCommit() {
					logger.info("CService事务提交后触发~最后一个业务");
				}
			});
		}
	}

}
一、首先我们需要了解 afterCommit 执行逻辑,如下:

1、org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction => commitTransactionAfterReturning(txInfo);

protected void commitTransactionAfterReturning(@Nullable TransactionInfo txInfo) {
    if (txInfo != null && txInfo.getTransactionStatus() != null) {
        if (logger.isTraceEnabled()) {
            logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
        }
        txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
    }
}

2、org.springframework.transaction.support.AbstractPlatformTransactionManager#commit => processCommit(defStatus) => triggerAfterCommit(status)

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
        boolean beforeCompletionInvoked = false;

        try {}
        catch (UnexpectedRollbackException ex) {}
        catch (TransactionException ex) {}
        catch (RuntimeException | Error ex) {}

        // Trigger afterCommit callbacks, with an exception thrown there
        // propagated to callers but the transaction still considered as committed.
        try {
            triggerAfterCommit(status);
        }
        finally {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
        }

    }
    finally {
        cleanupAfterCompletion(status);
    }
}

private void triggerAfterCommit(DefaultTransactionStatus status) {
    if (status.isNewSynchronization()) {
        TransactionSynchronizationUtils.triggerAfterCommit();
    }
}

观察 afterCommit 执行逻辑可知当 status.isNewSynchronization() 为 true 时才会执行事务提交后置处理。

通过Debug得知CService#exec 执行的时候 status.isNewSynchronization() 为 false 从而导致 afterCommit 没执行,然而 status.isNewSynchronization() 为啥为 false 呢。那我们需要了解下 TransactionInfo 构建

二、TransactionInfo 的构建

1、org.springframework.transaction.interceptor.TransactionAspectSupport#createTransactionIfNecessary => tm.getTransaction(txAttr)

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable 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) {
            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);
}

2、org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction => prepareTransactionStatus => newTransactionStatus

protected DefaultTransactionStatus newTransactionStatus(
			TransactionDefinition definition, @Nullable Object transaction, boolean newTransaction,
    boolean newSynchronization, boolean debug, @Nullable Object suspendedResources) {

    boolean actualNewSynchronization = newSynchronization &&
        !TransactionSynchronizationManager.isSynchronizationActive();
    return new DefaultTransactionStatus(
        transaction, newTransaction, actualNewSynchronization,
        definition.isReadOnly(), debug, suspendedResources);
}

可以看到 isNewSynchronization = newSynchronization && !TransactionSynchronizationManager.isSynchronizationActive() 。其中 newSynchronization 为 true ,只有 TransactionSynchronizationManager.isSynchronizationActive() 也是 true 才会导致 isNewSynchronization 为 false。那么问题就来到了 TransactionSynchronizationManager 事务管理器中为啥还有registerSynchronization?(AService 是在事务提交后 调用的 BService)

三、原因分析

此时可以将思路回到 AService 的 事务提交后处理

org.springframework.transaction.support.AbstractPlatformTransactionManager#commit => processCommit(defStatus) => triggerAfterCommit(status)

BService 的执行是在 triggerAfterCommit 中触发的

private void processCommit(DefaultTransactionStatus status) throws TransactionException {
    try {
        boolean beforeCompletionInvoked = false;

        try {}
        catch (UnexpectedRollbackException ex) {}
        catch (TransactionException ex) {}
        catch (RuntimeException | Error ex) {}

        // Trigger afterCommit callbacks, with an exception thrown there
        // propagated to callers but the transaction still considered as committed.
        try {
            triggerAfterCommit(status);
        }
        finally {
            triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
        }

    }
    finally {
        cleanupAfterCompletion(status);
    }
}

private void triggerAfterCommit(DefaultTransactionStatus status) {
    if (status.isNewSynchronization()) {
        TransactionSynchronizationUtils.triggerAfterCommit();
    }
}

通过上述代码可以看到 在 finally 中有个 triggerAfterCompletion 此方法 会触发 TransactionSynchronizationManager.clearSynchronization() 移除注册的事务同步。因此 Spring 中 AService 的 事务提交后处理 去调用 另一个 CService 再进行事务提交后处理 这样嵌套事务就无法执行。

四、解决方案

1、BService#exec 方法添加 注解:@Transactional(propagation = Propagation.NOT_SUPPORTED)

2、BService#exec 方法添加 注解:@Transactional(propagation = Propagation.REQUIRES_NEW)

3、调整BService到调用A的无事务的一层

其中 1、2 只所以可行 可见如下代码

org.springframework.transaction.support.AbstractPlatformTransactionManager#handleExistingTransaction => suspend => doSuspendSynchronization()

/**
 * Suspend all current synchronizations and deactivate transaction
 * synchronization for the current thread.
 * @return the List of suspended TransactionSynchronization objects
 */
private List<TransactionSynchronization> doSuspendSynchronization() {
    List<TransactionSynchronization> suspendedSynchronizations =
        TransactionSynchronizationManager.getSynchronizations();
    for (TransactionSynchronization synchronization : suspendedSynchronizations) {
        synchronization.suspend();
    }
    TransactionSynchronizationManager.clearSynchronization();
    return suspendedSynchronizations;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值