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

Posted 热爱编程的大忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了@Async可以和@Transactional结合使用吗?相关的知识,希望对你有一定的参考价值。

@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拦截器。

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

@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方法如下:

	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的关系了,本文比较简短,如果各位还有什么问题,可以在评论区提出。

以上是关于@Async可以和@Transactional结合使用吗?的主要内容,如果未能解决你的问题,请参考以下文章

Spring @Transactional 和 @Async

如何使用@Async 和@Transactional 在多线程中回滚?

具有@async超时值的Spring @transactional不起作用

从Transactional与Async同时使用的错误到动态代理

async 与 Thread 的错误结合

async 与 Thread 的错误结合