前言
往期文章:
- Spring IoC - Spring IoC 的設計
- Spring IoC - IoC 容器初始化 源碼解析
- Spring IoC - 依賴注入 源碼解析
- 向您生動地講解Spring AOP 源碼(1)
在上一章向您生動地講解Spring AOP 源碼(1)中,作者介紹了【開啓AOP自動代理的玄機】和【自動代理的觸發時機】。
在本章中,作者會向您介紹,Spring AOP 是如何解析我們配置的Aspect,生成 Advisors 鏈的?
閒話不多說,讓我們直接開始。
獲取對應 Bean 適配的Advisors 鏈
獲取對應 Bean 適配的 Advisors 鏈,分爲兩步。
- 獲取容器所有的 advisors 作爲候選,即解析Spring 容器中所有 Aspect 類中的 advice 方法,包裝成 advisor;
- 從候選的 Advisors 中篩選出適配當前 Bean的 Advisors 鏈;
未免讀者閱讀不連貫,我們重新貼一下上篇文章中我們最後講解的一段源碼,由此繼續往下講述。
源碼位置:AbstractAutoProxyCreator#wrapIfNecessary(..)
源碼位置:AspectJAwareAdvisorAutoProxyCreator#shouldSkip(..)
源碼位置:AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean(..)
、AbstractAdvisorAutoProxyCreator#findEligibleAdvisors(..)
可以看到兩個方法都調用了findCandidateAdvisors()
方法,也就是去獲取候選的 Advisors,我們進去看看裏面幹了什麼。
1. 獲取候選的 Advisors
從Debug 出來的線程棧可以看出,AnnotationAwareAspectJAutoProxyCreator
通過 持有 BeanFactoryAspectJAdvisorsBuilder
對象,來獲取Advisor鏈。
再往下看。源碼位置:BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
內容較長,請大家跟着註釋耐心看下去。
這個方法除了等下要講 advisorFactory.getAdvisors(..)
以外,需要注意的就是其爲了避免每次都去獲取所有的beanName,解析判斷,引入了緩存的機制;還有就是Aspect類是根據Spring Bean 是否被 @Aspect
註解修飾來判斷的。
我們接下去看,真正的去獲取我們的Advisor的方法,this.advisorFactory.getAdvisors(factory)
方法如下:
源碼位置:ReflectiveAspectJAdvisorFactory#getAdvisors(..)
解析advice 方法成 advisor對象,
源碼位置:ReflectiveAspectJAdvisorFactory#getAdvisor(..)
生成advisor
如何生成advisor也值得一提。
註釋方式下,我們聲明的advice 方法是這樣的。(不熟悉範例的可以看上一篇文章)
@Aspect
@Component
public class PointCutConfig {
// ... 省略
// service 層
@Pointcut("within(ric.study.demo.aop.svc..*)")
public void inSvcLayer() {}
// ... 省略
}
@Aspect
@Component
public class GlobalAopAdvice {
@Before("ric.study.demo.aop.PointCutConfig.inSvcLayer()")
public void logBeforeSvc(JoinPoint joinPoint) {
System.out.println("在service 層前打印日誌");
System.out.println("攔截的service 方法的方法簽名: " + joinPoint.getSignature());
}
}
生成之後是這樣的,
advice 的對象類型是InstantiationModelAwarePointcutAdvisorImpl
,我們來看下生成advisor時調用的這個類的構造函數,
裏面包括了一個重要的方法instantiateAdvice,即創建Advice,這也是我要強調的重點,怎麼解析出來一個advice。
源碼位置:InstantiationModelAwarePointcutAdvisorImpl#instantiateAdvice(..)
源碼位置:ReflectiveAspectJAdvisorFactory#getAdvice(..)
第一次解析Advisor的時機
關於第一次解析Advisor的時機,我剛開始也搞混了。所以在這裏說明一下。
這個圖是之前貼過的,第一次觸發的截圖。
AnnotationAwareAspectJAutoProxyCreator
繼承了AbstractAutoProxyCreator
實現了InstantiationAwareBeanPostProcessor
接口:
會在生成target class 對象之前,調用 postProcessBeforeInstantiation(..)
,具體的代碼可以去看AbstractAutowireCapableBeanFactory#createBean(..)
方法。我們這邊直接看一下 postProcessBeforeInstantiation(..)
在AbstractAutoProxyCreator
中的實現。
2. 篩選 出 適配當前類的 Advisors
這裏來一條分割線,至此,findCandidateAdvisors()
算是解析完畢了。
但是我們通過這個方法只是獲得了所有候選的advisors,還記得我們這一節的標題不?
【獲取對應 Bean 適配的Advisors 鏈】
那麼我們下一步就是要過濾出適配當前這個 target class 的 advisors。
也就是上圖的findAdvisorsThatCanApply(..)
Search the given candidate Advisors to find all Advisors that can apply to the specified bean.
從給出的候選 Advisors 找出可以作用在 當前bean 的 Advisors 鏈
Debug階段,篩選之前的候選 advisors 和篩選之後的可用的 advisors,
源碼位置:AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply(..)
源碼位置:AopUtils#findAdvisorsThatCanApply(..)
我們接下去看篩選的關鍵方法``AopUtils#canApply(…)`
篩選的工作主要由 ClassFilter 和 MethodMatcher 完成,比如AspectJExpressionPointcut的實現了ClassFilter和MethodMatcher接口,最終由AspectJ表達式解析,這個地方就複雜了,也不是核心點。
又是一條分割線。
到這裏之後,Advisor的篩選過程我們算是講完了。
經過排序之後,我們算是拿到了這個目標類使用的 Advisors 鏈。
小結
到這裏,大家可以回顧一下,我們總算是把TODO-1
【Spring AOP 如何 獲取對應 Bean 適配的Advisors 鏈】介紹完畢了,總結一下核心邏輯就是:
- 獲取當前 IoC 容器中所有的 Aspect 類
- 給 每個Aspect 類的advice 方法創建一個 Spring Advisor,這一步又能細分爲
- 遍歷所有advice 方法
- 解析方法的註解和pointcut
- 實例化 Advisor 對象
- 獲取到 候選的 Advisors,並且緩存起來,方便下一次直接獲取
- 從候選的 Advisors 中篩選出與目標類 適配的Advisor
- 獲取到 Advisor 的 切入點 pointcut
- 獲取到 當前 target 類 所有的 public 方法
- 遍歷方法,通過 切入點 的 methodMatcher 匹配當前方法,只有有一個匹配成功就相當於當前的Advisor 適配
- 對篩選之後的 Advisor 鏈進行排序
- 結束
下一節中,我們會介紹 【代理類的創建過程】,我們下次再會。