天天看點

你知道@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架構提供的一些便攜操作來完成更加進階的實作。

繼續閱讀