前言
最近,很多同学碰到了下面这个问题,添加了Spring提供的一个异步注解@Async
循环依赖无法被解决了,下面是一些读者的留言跟群里同学碰到的问题:
![cddb6e9eedfce9c4eb3941d83fc8ce64.png](https://i-blog.csdnimg.cn/blog_migrate/998f44b6142b4f4abc0ae94d2dc53692.png)
![cfa8928948c1a4f4089ebc5af2581bfa.png](https://i-blog.csdnimg.cn/blog_migrate/b727dcac14b028e7b7aa3cd4487c7ad4.png)
本着讲一个知识点就要讲明白、讲透彻的原则,我决定单独写一篇这样的文章对@Async
这个注解做一下详细的介绍,这个注解带来的问题远远不止循环依赖这么简单,如果对它不够熟悉的话建议慎用。
文章要点
![d64e3172d1fd6e52e8b8a06547c920a6.png](https://i-blog.csdnimg.cn/blog_migrate/f4cdbefc93ae413993d43a1ba9cc5beb.png)
@Async的基本使用
这个注解的作用在于可以让被标注的方法异步执行,但是有两个前提条件 1. 配置类上添加@EnableAsync注解 2. 需要异步执行的方法的所在类由Spring管理 3. 需要异步执行的方法上添加了@Async注解 我们通过一个Demo体会下这个注解的作用吧 第一步,配置类上开启异步:@EnableAsync@Configuration@ComponentScan("com.dmz.spring.async")public class Config {
}
第二步,
@Component // 这个类本身要被Spring管理public class DmzAsyncService {
@Async // 添加注解表示这个方法要异步执行public void testAsync(){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("testAsync invoked");
}
}
第三步,测试异步执行
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Config.class);
DmzAsyncService bean = ac.getBean(DmzAsyncService.class);
bean.testAsync();
System.out.println("main函数执行完成");
}
}// 程序执行结果如下:// main函数执行完成// testAsync invoked
通过上面的例子我们可以发现,
DmzAsyncService
中的
testAsync
方法是异步执行的,那么这背后的原理是什么呢?我们接着分析
原理分析
我们在分析某一个技术的时候,最重要的事情是,一定一定要找到代码的入口,像Spring这种都很明显,入口必定是在@EnableAsync
这个注解上面,我们来看看这个注解干了啥事(本文基于
5.2.x
版本)
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented// 这里是重点,导入了一个ImportSelector@Import(AsyncConfigurationSelector.class)public @interface EnableAsync {
// 这个配置可以让程序员配置需要被检查的注解,默认情况下检查的就是@Async注解
Class extends Annotation> annotation() default Annotation.class;// 默认使用jdk代理boolean proxyTargetClass() default false;// 默认使用Spring AOPAdviceMode mode() default AdviceMode.PROXY;// 在后续分析我们会发现,这个注解实际往容器中添加了一个// AsyncAnnotationBeanPostProcessor,这个后置处理器实现了Ordered接口// 这个配置主要代表了AsyncAnnotationBeanPostProcessor执行的顺序int order() default Ordered.LOWEST_PRECEDENCE;
}
上面这个注解做的最重要的事情就是导入了一个
AsyncConfigurationSelector
,这个类的源码如下:
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME ="org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";@Override@Nullablepublic String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
// 默认会使用SpringAOP进行代理case PROXY:return new String[] {ProxyAsyncCo