一、@Async 简介
从Spring3开始提供了@Async注解,被该注解标注的方法,Spring底层会新建一个线程池或者使用已有的线程池中的线程去异步的执行被标注的方法。
二、@Async 工作原理
@Async与@Transactional 工作原理基本是一样的,也是通过Spring AOP动态代理去实现的。Spring容器启动初始化bean时,判断类中是否使用了@Async注解,如果使用了则为其创建代理对象,在线程调用@Async注解标注的方法时,通过代理对象执行切入点处理器invoke方法,将方法的执行提交给线程池中的另外一个线程来处理,从而实现了异步执行。
三、在SpringBoot中的使用
@Async在SpringBoot中的使用还是挺简单的,只需要下面两部操作就可以实现方法的异步调用。
1. 在启动类上使用@EnableAsync注解
2. 将需要异步执行的方法所在的类注入到spring容器,并在异步的方法上添加@Async注解
四、@Async失效场景
五、@Async 底层源码解析
5.1 @Async注解源码
从上面@Async注解源码可以看出,只有一个value属性,这个属性就是线程池Bean的名称,如果在注解中设置了这个值,则根据这个值从Spring容器中获取这个线程池的Bean去异步执行方法;如果没设置,则会使用默认的线程池去异步执行该方法。所以这里想要指定具体的线程池去异步执行方法的话,只需要在@Async(“taskExecutor”)中设置线程池的bean的名称当作注解的value属性值就好。
5.2 @EnableAsync注解源码
(1)根据上面@EnableAsync源码可以看出,该注解中使用了一个@Import注解导入了一个AsyncConfigurationSelector.class类。
(2)进入到AsyncConfigurationSelector类,可以看到该类继承了AdviceModeImportSelector抽象类,AdviceModeImportSelector抽象类又实现了ImportSelector接口。并且在selectImports()方法中根据不同的AdviceMode来选择和返回适当的导入语句。
(3)进入到ProxyAsyncConfiguration配置类中,可以看到会创建一个AsyncAnnotationBeanPostProcessor对象,并设置相关的属性后返回该对象。
(4)再进入到AsyncAnnotationBeanPostProcessor类中,有个setBeanFactory()方法,在这个方法中往BeanFactory容器中增加了一个AsyncAnnotationAdvisor增强器。
(5)进入这个AsyncAnnotationAdvisor增强器的构造函数可以看到,分别创建了一个通知以及切入点。
(6)在这个创建增强方法的的函数中,可以看到创建了一个拦截器,主要通过这个拦截器来拦截标注有@Async注解的方法。
(7)进入到AnnotationAsyncExecutionInterceptor拦截器,可以看到其继承了AsyncExecutionInterceptor,主要的拦截方法就在这个AsyncExecutionInterceptor父类的invoke()方法中。主要步骤也就下面三步:1. 获取线程池;2. 创建线程;3. 提交任务到线程去执行。
(8)看下获取线程池的处理逻辑。主要步骤是:1.先从缓存中去查找,如果缓存中存在线程池就直接返回这个线程池去使用;2.如果缓存中没找到再根据方法的@Async注解value值进行判断;3.方法的的@Async注解设置了value值,则把value的值当做线程池的bean名称去Spring容器中找到这个bean并返回去使用;4.如果方法的@Async注解没有设置了value值,则使用默认的线程池去调用。