分析過程:
- 開啟異步代理
- 初始化excutor和exceptionHandler
- 定義切面處理
- 線程處理
@EnableAsync
@EnableAsync是開啟某個模塊的功能加載,之前在《導圖梳理springboot手動、自動裝配,讓springboot不再難懂》介紹過,@EnableXXX一般都有兩種用法,一種直接引入配置,一種可以通過注解的元數據選擇需要導入的配置。這里的@EnableAsync明顯屬於第二種。

這個EnableAsync注解,一般注解屬性我們都不需要改,默認就行了。那么這里最重要的代碼就只有一行了,就是這個@Import(AsyncConfigurationSelector.class),導入AsyncConfigurationSelector配置。
AsyncConfigurationSelector
我們打開AsyncConfigurationSelector看看:

上面的源碼里面,我在開頭寫了2個關鍵點,
- 一個是父類AdviceModeImportSelector
- 二是導入配置類ProxyAsyncConfiguration
大家在源碼分析的時候這樣去看源碼,當你打開一個類的時候,里面有很多個方法,你不知道你應該從哪個方法看起,你第一時間可以先去看繼承關系,就比如這AsyncConfigurationSelector類,他繼承了AdviceModeImportSelector,但AdviceModeImportSelector其實又實現了ImportSelector,而ImportSelector是我們比較熟悉的接口。我們知道ImportSelector里面有個方法selectImports(AnnotationMetadata)是用於根據注解屬性導入配置的,所以這里就是我們的突破口。
所以綜合來看源碼的方法是這樣的:
當你打開一個類,比較迷茫的時候:
- 第一步:打開類的繼承關系圖(Ctrl+Shift+Alt+u)

要注意spring的Aware系列接口,比如:
- BeanNameAware :可以獲取容器中bean的名稱
- BeanFactoryAware:獲取當前bean factory這也可以調用容器的服務
- ApplicationContextAware:當前的applicationContext, 這也可以調用容器的服務
- MessageSourceAware:獲得message source,這也可以獲得文本信息
- applicationEventPulisherAware:應用事件發布器,可以發布事件,
- ResourceLoaderAware:獲得資源加載器,可以獲得外部資源文件的內容;
這些都有一些特殊的功能,在spring項目被啟動的時候會被調用對應的實現接口,所以看到這些Aware實現類的時候,如果你比較迷茫,可以從Aware的實現類的接口重寫開始看。
而剛才我們說到的ImportSelector也是spring的一個比較特殊的接口了,我們就從selectImports(AnnotationMetadata)方法看起:

所以從這個方法看起你就很容易梳理到代碼了。上面代碼中,首先把EnableAsync的元數據都封裝到AnnotationAttributes中,然后再獲取到AdviceMode,最后選擇出需要導入的配置,而導入的配置方法是個抽象方法selectImports(AdviceMode),由子類重寫。子類自然就是AsyncConfigurationSelector,所以你才可以看到AsyncConfigurationSelector里有個selectImports方法,其實是重寫了父類的。
現在邏輯相對清晰了,由於AdviceMode默認就是AdviceMode.PROXY,所以我們導入的配置就是ProxyAsyncConfiguration,接下來我們再去分析。
ProxyAsyncConfiguration
這看配置類的名稱,翻譯過來就是代理異步配置。有時候我們看源碼也要從一個類的名稱去猜測可能的功能。我們之前在第二篇文章中猜想過,應該有個Aop切面處理@Async注解,如果大家熟悉aop的原理的話,aop也是使用的了代理。那么應該就是在這個配置類里面實現的了。
好了,由一個名字我想了這么多,接下來先看下代碼:

上面我依然點出了兩個重點:
- 1、父類AbstractAsyncConfiguration
- 2、初始化對象AsyncAnnotationBeanPostProcessor
- 為什么一直強調父類,因為子類初始化之前,父類是要先完成初始化的,所以加載順序都是父類先加載,這點必須清楚,另外就是子類一般都要重寫父類的方法,重寫的方法一般在父類的其他方法中會被調用。
既然是繼承關系,我們依然來看下繼承關系圖:

Aware系列的接口之一ImportAware,作用是通過實現ImportAware接口獲取對應注解的元數據。
所以,我們先去看完父類AbstractAsyncConfiguration,然后再回頭來看子類ProxyAsyncConfiguration。

1、setImportMetadata方法里讀取了EnableAsync的元數據存在了AnnotationAttributes 中。2、setConfigurers導入自定義的AsyncConfigurer配置類。
我們在第一篇文中就自定義了線程池,還有異步線程的錯誤處理器等,就是通過實現AsyncConfigurer接口實現的,而我們自定義的類就會被注入到setConfigurers這個方法中,然后被賦值給當前類的executor和exceptionHandler。
所以這個父類中,其實就是一些初始化,初始化this.enableAsync、this.executor和this.exceptionHandler。
當然了,我們不是必須要實現AsyncConfigurer重寫executor和exceptionHandler,所以this.executor和this.exceptionHandler可能還是為null的。
我們再回到ProxyAsyncConfiguration的asyncAdvisor()方法,看這個方法名稱,有點異步切面的意思呀,那么返回值AsyncAnnotationBeanPostProcessor是否就是一個切面增強類呢?這個我們去看下繼承關系。

繼承的東西比較多,先來說說我們比較熟悉的東西:
- BeanClassLoaderAware - 獲取當前類的類加載器
- BeanFactoryAware - 獲取Spring的核心容器BeanFactory
- BeanPostProcessor - bean初始化過程中的前置、后置處理器
- AbstractAdvisingBeanPostProcessor - 生成aop代理的后置處理器
那么現在來梳理一下邏輯,首先ProxyAsyncConfiguration中會開始初始化AsyncAnnotationBeanPostProcessor,因為是@Bean,所以在對象注入spring容器之前,你先不用看aware系列,不用看BeanPostProcessor,先看@Bean里面方法的內容,那是注入spring容器之前可能做一些初始化。
而asyncAdvisor()方法中,關鍵的代碼其實也沒多少,邏輯如下:1、就是new一個AsyncAnnotationBeanPostProcessor對象 2、bpp.configure(this.executor,this.exceptionHandler);就是賦值excutor和exceptionHandler:
- AsyncAnnotationBeanPostProcessor#configure

3、bpp.setAsyncAnnotationType(customAsyncAnnotation);如果有自定義的異步注解就賦值
然后就返回了對象,通過@Bean注解,這時候這個new出來的AsyncAnnotationBeanPostProcessor對象就會注入到spring容器中,進而調用aware和beanPostProcessor那一套流程。
AsyncAnnotationBeanPostProcessor
接下來就是重點之中的重點了,可以說@Async的重點核心就是這個類,之前做了這么多准備就是為了初始化這個類。
我們來回顧一下上面的內容,首先我們獲得了自定義的excutor和exceptionHandler,然后新建了AsyncAnnotationBeanPostProcessor對象並注入到了spring容器中,因為bean的生命周期比較復雜。
我怕很多人沒研究過spring的容器,對spring bean的聲明周期不太了解,特意從網上找了一張總結的圖,讓大家一張圖搞懂Spring bean的生命周期,從Spring容器啟動到容器銷毀bean的全過程。

通過這個圖,我們再回到我們的這個AsyncAnnotationBeanPostProcessor這個類的繼承關系圖,你就知道了執行的順序流程如下:
- 1、因為實現了BeanPostProcessor,所以先執行postProcessBeforeInitialization
- 2、執行構造器方法
- 3、執行BeanFactoryAware 、BeanClassLoaderAware的對應方法
- 4、執行BeanPostProcessor的postProcessAfterInitialization方法
ok,順序已給出,那么初始化的過程就清晰了,接下來我們只需要一步一步去看對應模塊的代碼。
- 第一步:postProcessBeforeInitialization
好像啥都沒做,忽略

- 第二步:構造器
其實對象我們是new出來的,然后再通過@Bean注入容器的,並不是使用@Component或者xml方式注入,所以構造器應該是早就執行了

這個構造器只有一行代碼,是說是不是在其他已存在的aop之前執行,參數表示是的。
- 第三步:BeanClassLoaderAware、BeanFactoryAware
因為BeanClassLoaderAware是aop代碼部分的了,是為了給對象生成代理的時候統一類加載器。所以這個方法我們不需要看。
我們來看下BeanFactoryAware的setBeanFactory方法:
- AsyncAnnotationBeanPostProcessor#setBeanFactory

代碼中,除了引入beanFactory之外,還定義了一個切面advisor ,並把切面advisor賦值給當前對象。
我們中篇文中說過,用編碼實現一個aop,需要准備幾個東西:
- ProxyFactory 代理工廠
- Pointcut 切點
- Advice 通知
- Advisor 切面
- Target 被代理對象
有了這幾個組件之后,我們就可以構建成一個aop。
那么再看這里代碼,這里的advisor就在這里初始化獲取到了。而我們可以這樣理解:Advisor = pointcut + Advice ,所以可說,我們完成了切面的初始化,其實也是@Async核心重要的一部分了。
ok,有了知識儲備,搞啥都清晰。我們接着往下面走, 看AsyncAnnotationAdvisor的初始化過程先,也就是構造方法:

上面重點是這兩行:

切面等於切點加通知處理。就是這兩樣東西了。也就是構造器里面其實得到了切點和通知。接下來我們繼續看着兩個方法:
- AsyncAnnotationAdvisor#buildAdvice

我們先來看下AnnotationAsyncExecutionInterceptor的繼承關系:

這里面我們比較熟悉的類有Advice、Interceptor、BeanFactoryAware。結合第二篇文章中講到的生成aop的編碼實現。你基本可以確定,這個AnnotationAsyncExecutionInterceptor類就是我們環繞通知的處理類了,Advice說明了這個類是個aop通知處理類,Interceptor說明了處理的方法是攔截器的invoke方法。切面攔截到切點時候就會到這個方法的invoke中執行對應的業務處理邏輯。
那么對應到@Async,執行的邏輯應該就是起一個線程執行方法。
清楚了這一點之后,我們再回到AnnotationAsyncExecutionInterceptor構造方法中,最終調用的是父類中的構造方法:

這里就是給executor和exceptionHandler一個初始化的執行器或錯誤處理器,初始化默認處理器之后再執行interceptor.configure(executor, exceptionHandler);

這意思就是如果我們之前自定義了執行器和錯誤處理器,那么用我們自定義的,如果沒有就用剛剛在構造器中初始化的默認的。
所以,切面的環繞通知處理Advice已經生成。我們再來看看另一個方法
- AsyncAnnotationAdvisor#buildPointcut

這生成切點的邏輯也挺簡單的,之前允許在@EnableAsync中通過annatation定義自定義的異步線程注解,我們常用的默認是@Async。所以這里意思其實是把所有的可能的注解都union起來,union就是合並意思。不管自定義的,還是默認的都作為切點。
這時候切點Pointcut已初始化好。
所以在AsyncAnnotationAdvisor中我們初始化好了Advice和Pointcut,而切面就等於Advice+Pointcut,那么它是一個切面來的嗎?我們來看下繼承關系:

果然實現了Advisor,是個切面。所以致此,我們已經定義了一個切面。
- 第四步:執行BeanPostProcessor的postProcessAfterInitialization方法

上面三步走完之后,我們定義得到了一個切面,接下來我們進入最后一步,就是bean的后置處理,這個后置處理器其實是aop中實現的,所以我們定義一個aop切面,其實都需要進入這個后置處理器,那么這里面做了什么事情呢?
看了里面的邏輯,應該就是直接給bean生成代理的了,那么我們寫的Async代碼中,那些需要我們生成代理呢,是不是所有寫了@Async注解的方法或者類?因為我知道aop通過ProxyFactory生成代理的,所以我在 ProxyFactory proxyFactory = prepareProxyFactory(bean, beanName); 這里打個端點,然后啟動項目。

然,需要代理的是UserServiceImpl,因為我的@Async方法都是寫在UserServiceImpl上的:

所以UserServiceImpl就是aop需要代理的對象。其中prepareProxyFactory的代碼如下:

就是創建proxyFactory對象,然后設置目標代理對象就是UserServiceImpl,然后接着走是設置interface的,

其中evaluateProxyInterfaces的內容是:

可以看到因為UserServiceImpl是個實現類,所以對應的接口需要聲明,這樣使用UserService調用方法時候才會觸發aop。所以這里面的重要代碼就是proxyFactory.addInterface(ifc);
然后方法繼續執行,就到了proxyFactory.addAdvisor(this.advisor);這一句,advisor就是我們上面初始化好的切面,這里直接set給proxyFactory,定義切點,和切面處理。
再接着走,到了customizeProxyFactory(proxyFactory);這一步其實可以重寫,然后proxyFactory自定義一些需要的屬性等。@Async中沒有重寫,所以這一步我們跳過。
最后到了代碼*return *proxyFactory.getProxy(getProxyClassLoader());這一步,是不是aop生成代理了。
所以總結我們上面所有的內容,我們再來梳理一下proxyFactory的偽代碼過程:

結合我們第二篇文章中的aop編碼實現方式,是不是很相似了。所以這時候aop我們已經完全定義好了。
接下來我們回頭來看環繞通知處理里面的業務邏輯,因為現在aop已經生成,攔截@Async之后我們需要異步處理代理的方法。這時候我們進入AnnotationAsyncExecutionInterceptor的invoke方法。
- 在父類中AsyncExecutionInterceptor#invoke

上面第一步獲取到executor,然后再通過Callable定義一個異步線程。然后把task放在doSubmit中執行。

這里面就明顯了,就是去執行我們的task,然后返回結果。具體的我們就不再去深究啦。相信到了這一步,我們已經明白了@Async的原理。