springboot使用@Async注解时异步方法或@Transactional注解事务不生效原因分析及解决方案
一、前言
很多小伙伴在初次使用springboot框架@Async注解时,可能会发现明明在方法上添加了@Async注解,并且也在启动类上添加了@EnableAsync注解,但是方法依旧没有异步的去执行。
二、思考
很大可能性是因为是在同一个类里面,一个方法去调用另外一个有@Async注解的方法,这种情况下异步方法是不会有效果的(@Transational也是同理)。
除此之外,在使用springboot框架执行异步方法时,有以下几点需要注意
- 必须在启动类中增加@EnableAsync注解;
- 异步类没有被springboot管理,在有异步方法的类上添加@Component注解(或其他注解)且保证可以扫描到异步类;
- 测试异步方法不能与异步方法在同一个类中;
- 测试类中需要使用spring容器初始化的异步类,不能自己手动new对象;
三、原因分析
上面的1、2、4点大家都会注意到,但是经常会忽视第3点,那么为什么在同一个类里面,用一个方法去调用另外一个有@Async注解的方法,异步方法不会生效呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @Service class A{ @Async method b(){...} method a(){ //标记1 b(); } } //Spring扫描注解后,会创建另外一个代理类,并对添加注解的方法根据切入点创建代理 //调用代理,执行切入点处理器invoke方法,实现异步执行 class proxy$A{ A objectA = new A(); method b(){ //标记2 //MethodInterceptor.invoke objectA.b(); } method a(){ //标记3 objectA.a(); //由于a()没有注解,所以不会创建代理,而是直接调用A的实例的a()方法 } } |
当我们调用A的bean的a()方法的时候,也是被proxy$A拦截,执行proxy$A.a()(标记3),然而,由以上代码可知,这时候它调用的是objectA.a(),也就是由原来的bean来调用a()方法了,所以代码跑到了“标记1”。由此可见,“标记2”并没有被执行到,所以startTransaction()方法也没有运行。
四、代码实践
根据上面的注意点,我们可以使用以下四种情况进行测试
- 1st:调用当前类中带有@Async注解的方法...
- 2nd:调用其他类Test类中带有@Async注解的方法...
- 3rd:调用其他类Test1类不带@Async注解的方法...
- 4th:调用其他类Test2类(没有被springboot初始化的类)中带有Async注解的方法...
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | @Slf4j @Component public class TestAsync { @Autowired private Test test; @Autowired private Test1 test1; public void testCommon(){ log.info( "主线程名:{}" +Thread.currentThread().getName()); log.info( "1st:调用当前类中带有@Async注解的方法..." ); this .testAsync(); log.info( "当前类名:{}" , this .getClass().getName()); log.info( "===========================================" ); log.info( "2nd:调用其他类Test类中带有@Async注解的方法..." ); test.testAsync(); Class c= test.getClass(); log.info( "当前类名:{}" ,c.getName()); log.info( "Test类的父类:{}" ,c.getSuperclass().getName()); log.info( "===========================================" ); log.info( "3rd:调用其他类Test1类不带@Async注解的方法..." ); test1.test(); log.info( "当前类名:{}" ,test1.getClass().getName()); log.info( "===========================================" ); log.info( "4th:调用其他类Test2类(没有被springboot初始化的类)中带有Async注解的方法..." ); Test2 test2 = new Test2(); test2.testAsync(); log.info( "当前类名:{}" ,test2.getClass().getName()); } @Async public void testAsync(){ log.info( "当前类调用线程名:" +Thread.currentThread().getName()); } } |
1 2 3 4 5 6 7 8 9 10 11 12 | @Slf4j @Component public class Test { @Async public void testAsync(){ log.info( "Test类线程名:" +Thread.currentThread().getName()); } public void method(){ log.info( "this is Test.method" ); } } |
1 2 3 4 5 6 7 | @Slf4j @Component public class Test1 { public void test() { log.info( "Test1类线程名:" +Thread.currentThread().getName()); } } |
1 2 3 4 5 6 | @Slf4j public class Test2 { public void testAsync() { log.info( "Test2类线程名:" +Thread.currentThread().getName()); } } |
测试结果如下:
通过类名可以看出当前只有情况2有效的,它是使用的代理类,并且该代理类是目标类的子类。
五、总结
这篇文章主要分析了在spingboot中可能导致@Async注解失败的几种情况以及如何解决。
文章参考:
https://blog.csdn.net/clementad/article/details/47339519?utm_source=copy
本博客文章均已测试验证,欢迎评论、交流、点赞。
部分文章来源于网络,如有侵权请联系删除。
转载请注明原文链接:https://www.cnblogs.com/sueyyyy/p/14212953.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix