在事务注解@Transactional的方法中如何做到在事务提交/回滚后执行业务逻辑

需求描述

在某些情况下 我们需要在事务注解的方法中,在事务提交/回滚的前后,去执行一些业务逻辑
如:在事务提交后执行一些异步任务


解决方案:

案例:

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

摸鱼的小张同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值