需求描述
在某些情况下 我们需要在事务注解的方法中,在事务提交/回滚的前后,去执行一些业务逻辑
如:在事务提交后执行一些异步任务
解决方案:
案例:
public void insert(XxxEntity entity){
XxxMapper.delete(entity);
// 在删除数据后发送异步消息
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
mqProducer.sendMessage("发送删除后消息");
}
}
);
}
我们可以通过Spring为我们提供的 TransactionSynchronizationManager.registerSynchronization(TransactionSynchronization synchronization)
方法来实现在事务提交/回滚后插入业务逻辑
其中我们查看源码注释的第一行Register a new transaction synchronization for the current thread.
可以发现,该方法是将当前线程注册一个事务synchronization 也就是将TransactionSynchronization注册到当前线程的synchronizations。
而TransactionSynchronization 则使用了TransactionSynchronizationAdapter
抽象类 可以发现该类中所有的方法都是空方法,我们可以根据需要重写里面的方法,可以发现这个类使用了适配器模式
通过这个类 我们可以再事务提交前/后执行一些业务逻辑
那么spring的事务是怎么调用这个方法的呢?
看过Spring源码的同学一定非常熟悉@Transactional注解是怎么将方法加入到事务中的
没错就是通过TransactionInterceptor
类的invoke()方法里
invokeWithinTransaction方法
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// 如果 transaction attribute 是null, 那么该方法不需要事务.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
PlatformTransactionManager ptm = asPlatformTransactionManager(tm);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
...
if (txAttr == null || !(ptm instanceof CallbackPreferringPlatformTransactionManager)) {
// 根据传播级别配置,看是否需要新建事务.
TransactionInfo txInfo = createTransactionIfNecessary(ptm, txAttr, joinpointIdentification);
Object retVal;
try {
// 调用真正的dao层逻辑
retVal = invocation.proceedWithInvocation();
} catch (Throwable var18) {
// 根据@Transactional配置的异常来决定是否回滚
this.completeTransactionAfterThrowing(txInfo, var18);
throw var18;
} finally {
// 结束当前的事务,信息是保存在ThreadLocal里
this.cleanupTransactionInfo(txInfo);
}
if (vavrPresent && VavrDelegate.isVavrTry(retVal)) {
// Set rollback-only in case of Vavr failure matching our rollback rules...
TransactionStatus status = txInfo.getTransactionStatus();
if (status != null && txAttr != null) {
retVal = VavrDelegate.evaluateTryFailure(retVal, txAttr, status);
}
// 提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
在上面的代码中 commitTransactionAfterReturning(txtexInfo); 这行代码 可以发现 TransactionInfo txtexInfo 该实例是TransactionAspectSupportd的一个内部类,最终是调用AbstractPlatformTransactionManager.commit()方法
在该方法中 可以发现最终调用processCommit()方法
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
boolean unexpectedRollback = false;
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
unexpectedRollback = status.isGlobalRollbackOnly();
status.releaseHeldSavepoint();
}
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
unexpectedRollback = status.isGlobalRollbackOnly();
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
// Throw UnexpectedRollbackException if we have a global rollback-only
// marker but still didn't get a corresponding exception from commit.
if (unexpectedRollback) {
throw new UnexpectedRollbackException(
"Transaction silently rolled back because it has been marked as rollback-only");
}
}
catch (UnexpectedRollbackException ex) {
// can only be caused by doCommit
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
throw ex;
}
catch (TransactionException ex) {
// can only be caused by doCommit
if (isRollbackOnCommitFailure()) {
doRollbackOnCommitException(status, ex);
}
else {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
}
throw ex;
}
catch (RuntimeException | Error ex) {
if (!beforeCompletionInvoked) {
triggerBeforeCompletion(status);
}
doRollbackOnCommitException(status, ex);
throw 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()) {
if (status.isDebug()) {
logger.trace("Triggering afterCommit synchronization");
}
TransactionSynchronizationUtils.triggerAfterCommit();
}
}
在提交成功之后触发triggerAfterCommit,这里调用了TransactionSynchronizationUtils.triggerAfterCommit()。
注意这里没有try catch,说明triggerAfterCommit的异常最终会抛给调用方
小结
使用TransactionSynchronizationManager.registerSynchronization可以在当前线程的事务注册一个TransactionSynchronizationAdapter,可以在afterCommit方法也就是事务提交成功之后执行一些额外业务逻辑;
注意这里抛出的异常不影响事务提交,但是异常不会被catch需要由调用方处理,对于afterCommit有数据库相关操作的建议使用PROPAGATION_REQUIRES_NEW这个事务传播级别,不然afterCommit的db操作可能不会生效。
参考
https://www.jianshu.com/p/dcfad3f80e3b
https://juejin.cn/post/7003614270877335560