天天看点

你知道@Async 是怎么让方法异步执行的吗?

作者:架构师面试宝典
你知道@Async 是怎么让方法异步执行的吗?

对于Spring的封装能力,大家应该是不陌生的,因此对于很多读者来讲,学会了它的使用,并不会去研究其底层的实现。例如@Async 注解,一般人可能只是注意到它是一个注解,并且在方法或者类上添加上整个注解之后,Spring会将对应的方法或者类中的所有方法都放到一个单独的线程池中去执行,通过这种方式来实现异步执行操作。但是对于@Async到底是如何运行的却不得而知,这篇文章就来跟大家一起学习其原理。

@Async

首先,需要只知道这个注解是用来标注开启异步执行操作的注解。对于这个注解本身来讲,除了依赖于Java底层的支持,还依赖了一些Spring相关的内容。通过反射的方式拦截到了标注有该注解的类,然后通过AOP来进行切面的拦截。进行切面拦击之后,就需要去实现异步方法的执行。带着这个思路我们来分析一下@Async 的原理。

如何开启异步操作呢?

在Spring中通过@EnableAsync注解来开启异步执行方法的操作。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AsyncConfigurationSelector.class)
public @interface EnableAsync {
	Class<? extends Annotation> annotation() default Annotation.class;
	boolean proxyTargetClass() default false;
	AdviceMode mode() default AdviceMode.PROXY;
	int order() default Ordered.LOWEST_PRECEDENCE;

}           

从代码中可以看到在@EnableAsync 中Import导入了一个AsyncConfigurationSelector ,这种引入也是Spring提供的引入第三方的依赖的方式。通过类名可以知道引入的是一个异步执行配置的选择器。这个默认选择器就是ProxyAsyncConfiguration

你知道@Async 是怎么让方法异步执行的吗?

在这个类中有如下的方法,注入了AsyncAnnotationBeanPostProcessor处理器。从名称上来看就是用来处理@Async 注解的处理器。

你知道@Async 是怎么让方法异步执行的吗?

在这个处理器中存在一个AnnotationAsyncExecutionInterceptor的拦截器,根据我们上面的分析可以知道,AOP的最外层是代理类,然后进行切面。通过Advisor创建切面。这些操作都是在AsyncAnnotationBeanPostProcessor,中来完成,如下图所示

你知道@Async 是怎么让方法异步执行的吗?

并且在AsyncAnnotationAdvisor中创建了advice和pointcut,并且在拦截器操作中就是对advice的处理。

你知道@Async 是怎么让方法异步执行的吗?

完成这些操作之后,关于@EnableAsync注解的作用就算完成了。它主要的操作就是利用AOP技术创建好一个切面,这个切面上所有的操作都是关于@Async的操作。

具体实现原理

在上面的介绍中,我们知道了@EnableAsync 的生效是通过AOP来实现的。但是在实现了AOP操作之后,就需要将异步操作放入到一个异步的线程池中去执行。下面我们就要去看看执行操作的线程池是在哪一步进行创建的。

在前面的分析中介绍了一个AnnotationAsyncExecutionInterceptor 拦截器类,需要创建线程池的操作就是在这个拦截器中完成的

你知道@Async 是怎么让方法异步执行的吗?

在其父类AsyncExecutionAspectSupport中完成了对执行线程池的创建。

你知道@Async 是怎么让方法异步执行的吗?

并且通过getDefaultExecutor 方法来获取到Spring容器中对应的执行的线程池Bean。当然在没有进行配置的情况下,默认使用的是SimpleAsyncTaskExecutor线程池。

提交执行任务执行

基于上面的分析,我们这回到,当方法被@Async注解的时候,也就是会被AnnotationAsyncExecutionInterceptor拦截器所拦截,并且通过代码分析我们找到了相关的拦截处理机制。在其父类AsyncExecutionInterceptor 中,对提交的任务进行验证分析处理。

你知道@Async 是怎么让方法异步执行的吗?

其中determineAsyncExecutor 中获取到的就是执行的executor与具体方法对象绑定的关系,并且对于每个添加了@Async的方法来讲都会有一个独立的executor,然后调用doSubmit方法执行,并且根据对应的returnType对返回值进行处理。

你知道@Async 是怎么让方法异步执行的吗?

自定义线程池

在上面的分析中,我们提到了可以通过配置的方式来自定义执行线程池。在SpringBoot中提供了一个AsyncConfigurer 接口来让开发人员自定义实现的线程池。可以通过继承AsyncConfigurerSupport类并且实现其中的方法来实现。需要注意的是,AsyncConfigurer在每个项目中只能有一个实现Bean实例。如果出现多个,则会抛出异常。

总结

上面我们介绍了关于@Async的相关执行原理,从整个的分析来看,基本上行基于Spring的一些优秀的实现项目都是离不开Spring的核心IOC和AOP操作。当然通过代码分析只是掌握其运行原理的一种途径,有兴趣的读者也可以利用Spring框架提供的一些便携操作来完成更加高级的实现。

继续阅读