前言
最近做项目的时候,一个业务方法里面有一段逻辑需要异步执行,于是我就把那段逻辑抽出来放在原业务方法下面作为一个新的方法并加上@Async标签,结果发现方法并没有异步去执行。为什么会这样呢?明明启动类上面也加了@EnableAsync标签,也加了async配置,其他地方也都是可以的。
原来
原来在同一个类里面,一个方法去调用另外一个有@Async注解的方法,是不会有效果的(@Transational也是同理)
是什么原因呢?
因为spring 在扫描bean的时候会扫描类里面是否有@Async注解,如果有spring就会为这个bean动态地生成一个代理类,这个代理类是继承原来那个bean并且重写了父类中被@Async注解的方法(如果注解在类上,则重写所有方法),并利用AOP切面为这些方法加上异步逻辑,于是这些方法被其他类调用的时候,其实是调用的代理类中重写过的方法,但是如果这些方法是被同一个类中的其他方法调用的就不会通过代理类了,而是直接通过当前对象去调,所以也就不生效了。
具体的@Async注解的源码分析可以参考这篇文章,写的挺仔细的,但是。。。也太仔细了吧,都是源码和巨长的类名,我都看晕了,以后失眠我就看看spring源码吧。。。
https://cloud.tencent.com/developer/article/1426027
一毛钱实验室
反正上面那篇文章我看了好久都没有看明白,于是决定自己写代码实验看看。
于是咻的一下就写了4个类来测试:
- TestAsync (测试主类,测试同一个类里面调用带Async注解的方法的效果)
- Test (被@Component修饰,包含带有@Async注解的方法)
- Test1 (被@Component修饰,不包含带有@Async注解的方法)
-
Test2 (不被@Component修饰,包含带有@Async注解的方法)
TestAsync调用其它三个类里面的方法,代码如下:
@Component
public class TestAsync {
@Autowired
private Test test;
@Autowired
private Test1 test1;
public void testCommon(){
System.out.println("主线程名:"+Thread.currentThread().getName());
System.out.println("~情况1、调用当前类this的带有Async注解的方法~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
this.testAsync();
System.out.println(this.getClass().getName());
System.out.println();
System.out.println("~情况2、调用其他类Test的带有Async注解的方法~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
test.testAsync();
Class c= test.getClass();
System.out.println(c.getName());
System.out.println("~Test的父类~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
Class pc = c.getSuperclass();
System.out.println(pc.getName());
System.out.println();
System.out.println("~情况3、调用其他类Test2(没有任何导致bean注入的注解)的带有Async注解的方法~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
Test2 test2 = new Test2();
test2.testAsync();
Class c2= test2.getClass();
System.out.println(c2.getName());
System.out.println();
System.out.println("~参照组、调用其他类Test1(类中没有Async注解)的方法~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
test1.test();
System.out.println(test1.getClass());
System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
}
@Async
public void testAsync(){
System.out.println("当前类调用线程名:"+Thread.currentThread().getName());
}
}
@Component
public class Test {
@Async
public void testAsync(){
System.out.println("Test类线程名:"+Thread.currentThread().getName());
}
}
@Component
public class Test1 {
public void test() {
System.out.println("Test1类线程名:"+Thread.currentThread().getName());
}
}
@Component
public class Test1 {
public void test() {
System.out.println("Test1类线程名:"+Thread.currentThread().getName());
}
}
最终打印结果是:
主线程名:http-nio-8080-exec-1
~情况1、调用当前类this的带有Async注解的方法~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
当前类调用线程名:http-nio-8080-exec-1
com.lee.testeverything.Util.TestAsync
~情况2、调用其他类Test的带有Async注解的方法~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
com.lee.testeverything.Util.Test$$EnhancerBySpringCGLIB$$7418b27f
~Test的父类~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
com.lee.testeverything.Util.Test
~情况3、调用其他类Test2(没有任何导致bean注入的注解)的带有Async注解的方法~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Test2类线程名:http-nio-8080-exec-1
com.lee.testeverything.Util.Test2
~参照组、调用其他类Test1(类中没有Async注解)的方法~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Test1类线程名:http-nio-8080-exec-1
class com.lee.testeverything.Util.Test1
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Test类线程名:task-1
可以看到只有情况2有效的,并且它是使用的代理类,并且该代理类是目标类的子类
总结
1、原来在同一个类里面,一个方法去调用另外一个有@Async注解的方法,是不会有效果的(@Transational也是同理)
2、有@Async注解的方法所在的类必须在spring容器初始化的时候就能被加载到容器里面去的,比如带@Component注解,@Service注解之类的。
网友评论