一、前言
很多小伙伴在初次使用springboot框架@Async注解时,可能会发现明明在方法上添加了@Async注解,并且也在启动类上添加了@EnableAsync注解,但是方法依旧没有异步的去执行。
二、思考
很大可能性是因为是在同一个类里面,一个方法去调用另外一个有@Async注解的方法,这种情况下异步方法是不会有效果的(@Transational也是同理)。
除此之外,在使用springboot框架执行异步方法时,有以下几点需要注意
必须在启动类中增加@EnableAsync注解;异步类没有被springboot管理,在有异步方法的类上添加@Component注解(或其他注解)且保证可以扫描到异步类;测试异步方法不能与异步方法在同一个类中;测试类中需要使用spring容器初始化的异步类,不能自己手动new对象;
三、原因分析
上面的1、2、4点大家都会注意到,但是经常会忽视第3点,那么为什么在同一个类里面,用一个方法去调用另外一个有@Async注解的方法,异步方法不会生效呢?
原来,spring 在扫描bean的时候会扫描该类的方法上是否包含@Async 注解,如果包含,spring会为这个bean动态地生成一个子类(即代理类proxy),代理类是继承原来那个bean的,并且重写了父类中被@Async 注解的方法(如果该注解是加在了类上,则会重写该类的所有方法),并利用AOP切面为这些方法加上异步逻辑。
此时,当这个有注解的方法被调用的时候,实际上调用的是代理类中重写过的方法。然而,如果这个有注解的方法是被同一个类中的其他方法调用的,那么就不会调用该代理类了,而是直接通过当前对象去调,所以也就不生效了,所以我们看到的现象就是该异步方法没有生效。
为什么一个方法a()调用同一个类中另外一个方法b()的时候,b()不是通过代理类来调用的呢?可以看下面的例子(为了简化,用伪代码表示):
@Service class A{ @Async method b(){...} method a(){ //标记1 b(); } } //Spring扫描注解后,会创建另外一个代理类,并对添加注解的方法根据切入点创建代理 //调用代理,执行切入点处理器invoke方法,实现异步执行 class proxy$A{ A