有一段比较有意思的代码。
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;
}