Spring扩展接口之afterCommit(同一方法中如何设置在事务提交后执行相关代码)


  • 背景

  • 解决方式

  • spring事务提交扩展点

    • 1. 直接利用spring提供的注册api

    • 发送事务提交完成事件

  • 总结

  • 参考


背景

有时候我们会有这样的业务场景,比如在同一个方法中,想前面的事务执行完,再执行后面的方法。语言描述比较抽象,我们直接看代码


 @Autowired
    @Qualifier(value = "applicationThreadPoolTaskExecutor")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    
    @Transactional(rollbackFor = Exception.class)
    public void savePackageConfig(PackageConfigDTO dto)
{
  
  // 数据库操作 伪代码 保存id为1的数据
  dao.save(dto);
  dao.update(dto);
  // 由于下面的业务耗时很长,所以使用异步
  threadPoolTaskExecutor.submit(() -> {
   //查询id为1的数据
   PackageConfigDO configDO = dao.select(id);
   // 使用configDO执行业务操作
  
  });
    }

上面的代码逻辑解释也很清晰,就是在执行完保存数据后,会异步再去查询前面保存的数据,可能会导致在方法事务还没有提交的时候异步线程去查询,由于使用的是线程池,所以不算同一个事务session,事务的隔离级别为RC,异步线程查询不到未提交的事务而获取不到数据configDO而报错

解决方式

spring事务提交扩展点

我们先来简单从源码看看spring事务提交留下的扩展点 我们看看AbstractPlatformTransactionManager的cmom

 public final void commit(TransactionStatus status) throws TransactionException {
 // ... 省略前面代码
  processCommit(defStatus);

}

 private void processCommit(DefaultTransactionStatus status) throws TransactionException {
 // 事务提交
 // ... 代码省略
// 事务提交后扩展点核心方法   triggerAfterCompletion(status,TransactionSynchronization.STATUS_ROLLED_BACK);

}

private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {
  if (status.isNewSynchronization()) {
   // 这里可以看到在事务提交后会执行 TransactionSynchronization接口的代码,所以只需要注册一个接口到List<TransactionSynchronization>中
   List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
   TransactionSynchronizationManager.clearSynchronization();
   if (!status.hasTransaction() || status.isNewTransaction()) {
    if (status.isDebug()) {
     logger.trace("Triggering afterCompletion synchronization");
    }
    // No transaction or new transaction for the current scope ->
    // invoke the afterCompletion callbacks immediately
    invokeAfterCompletion(synchronizations, completionStatus);
   }
   else if (!synchronizations.isEmpty()) {
    // Existing transaction that we participate in, controlled outside
    // of the scope of this Spring transaction manager -> try to register
    // an afterCompletion callback with the existing (JTA) transaction.
    registerAfterCompletionWithExistingTransaction(status.getTransaction(), synchronizations);
   }
  }
 }

通过源码我们发现了解决方式,即可通过注册接口List<TransactionSynchronization>中 说到底就是利用Spring留下的扩展接口afterCommit

1. 直接利用spring提供的注册api

// TransactionSynchronizationAdapter是TransactionSynchronization的默认实现
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
    @Override
    public void afterCommit() {
        // 事务提交后需要执行的业务逻辑
    }
});

上面的代码就改成这样的

TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                // 事务提交后需要执行的业务逻辑: 发消息, 日志...
                threadPoolTaskExecutor.submit(() -> {
                    // 业务操作
                    }});
            }
        });

发送事务提交完成事件

这里可以利用spring提供的ApplicationEventPublisher去发布一个事务完成事件,供其他方法订阅。

这种方式感觉相对麻烦,不推荐使用

使用方式和Guava观察者模式有些类似

@Autowired
private ApplicationEventPublisher publisher;
 
@Override
@Transactional(rollbackFor = Exception.class)
public void add(AdvanceChargeApplyAddInput input
{
    this.save(advanceChargeApply);
    // 发送事件
    publisher.publishEvent(advanceChargeApply);
}
// 响应事件, 事务提交后执行
@TransactionalEventListener
public void handle(PayloadApplicationEvent<AdvanceChargeApply> event) {
    System.out.println("TransactionalEventListener 事务提交后执行");
}

总结

Spring事务使用起来给我的感觉还是挺复杂的,使用起来使用看似给我们都是封装和好,实际有很多坑需要处理,最常见的就是

@Transactional
    public void savePackageConfig(PackageConfigDTO dto){
    // crud
    throw new Exception();
}

这里的事务会回滚吗?很明显不会,因为Transactional默认只会回滚RuntimeExceptionError,所以要想使用好事务,还是需要自己对事务有更深入的认识,不然随时都可能掉坑里

参考

简书


原文始发于微信公众号(小奏技术):Spring扩展接口之afterCommit(同一方法中如何设置在事务提交后执行相关代码)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/30431.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!