前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >@Async可以和@Transactional结合使用吗?

@Async可以和@Transactional结合使用吗?

作者头像
大忽悠爱学习
发布2022-10-24 15:48:29
发布2022-10-24 15:48:29
3.5K00
代码可运行
举报
文章被收录于专栏:c++与qt学习c++与qt学习
运行总次数:0
代码可运行

@Async可以和@Transactional结合使用吗?


前言

在编写Spring在多线程环境下如何确保事务一致性时,我突然联想到@Async注解,心里就在盘算着@Async注解能否和@Transactional注解一起使用呢?

当然,这里也是再看到了异步事务?关于异步@Async + 事务@Transactional的结合使用问题分析【享学Spring MVC】文章后,才想着对该问题作出一个彻底的研究,也是帮助其他小伙伴解开心头之惑。


结论

这里就不花费时间进行结论验证了,具体验证可以看下面这篇文章:

异步事务?关于异步@Async + 事务@Transactional的结合使用问题分析【享学Spring MVC】

我这边把上文中的结论整理一下,如下:

  • @Async注解的方法上,再标注@Transactional注解,事务依旧是生效的
  • 不同线程之间的事务完全隔离
  • 异步线程内仍是可以调用异步

原理

这里的原理只挑核心讲,想要彻底搞清楚原理,需要先把@Async注解实现原理和@Transactional注解的实现原理都弄清楚,Spring在多线程环境下如何确保事务一致性文中都已经将相关原理关联的阅读资源给出,不清楚的可以去查看一番。

@Async和@Transactional注解都是通过Spring aop实现的,核心都是靠着关键的MethodInterceptor实现,@Async会给对应bean代理对象中放入一个AnnotationAsyncExecutionInterceptor拦截器,而@Transactional会给对应bean的代理对象中放入一个TransactionInterceptor拦截器。

下面为了验证,我先给出一个使用例子:

代码语言:javascript
代码运行次数:0
复制
@Service
@RequiredArgsConstructor
public class TestService {
    private final IRoleService iRoleService;
    private final IAuthorityService iAuthorityService;

    /**
     * 测试方法
     */
    @Transactional
    @Async
    public void test() {
        iRoleService.removeById(1);
        iAuthorityService.removeById(1);
        throw new RuntimeException("我就要抛出异常");
    }
}

@SpringBootTest(classes = AuthenticationCenterMain.class)
public class Test {
    @Resource
    private TestService testService;

    @SneakyThrows
    @org.junit.jupiter.api.Test
    public void test(){
      testService.test();
    }

}

我们通过断点来查看一下为TestService生成的代理对象是什么模样:

可以看到是@Async注解提供的拦截器排在前面,而@Transactional注解提供的拦截器排在后面,因此可以知道,test方法事务过程的执行,是在@Async注解提供的某个异步线程内实现的。


AsyncExecutionInterceptor拦截器提供的invoke方法如下:

代码语言:javascript
代码运行次数:0
复制
	public Object invoke(final MethodInvocation invocation) throws Throwable {
		...
		//决定使用哪一个异步线程池来执行当前标注有@Async注解的方法
		AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
		...
        //将过滤器链的执行包装为一个Callable任务 
		Callable<Object> task = () -> {
			try {
			   //过滤器链中此时还有一个TransactionInterceptor拦截器没有执行
			   //该拦截器执行完后,最终执行目标方法
				Object result = invocation.proceed();
				if (result instanceof Future) {
					return ((Future<?>) result).get();
				}
			}
			catch (ExecutionException ex) {
				handleError(ex.getCause(), userDeclaredMethod, invocation.getArguments());
			}
			catch (Throwable ex) {
				handleError(ex, userDeclaredMethod, invocation.getArguments());
			}
			return null;
		};
        //提交任务到线程池执行
		return doSubmit(task, executor, invocation.getMethod().getReturnType());
	}

TransactionInterceptor提供的invoke方法源码就不分析了,在Spring在多线程环境下如何确保事务一致性一文已经进行了分析。


小结

到此,我相信各位也基本清楚了@Async和@Transactional的关系了,本文比较简短,如果各位还有什么问题,可以在评论区提出。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-10-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • @Async可以和@Transactional结合使用吗?
  • 前言
  • 结论
  • 原理
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档