前言
往期文章:
- Spring IoC - Spring IoC 的設(shè)計
- Spring IoC - IoC 容器初始化 源碼解析
- Spring IoC - 依賴注入 源碼解析
- Spring AOP - 注解方式使用介紹(長文詳解)
- 向您生動地講解Spring AOP 源碼(1)
在上一章向您生動地講解Spring AOP 源碼(1)中蔚万,作者介紹了【開啟AOP自動代理的玄機】和【自動代理的觸發(fā)時機】儒喊。
在本章中,作者會向您介紹逛艰,Spring AOP 是如何解析我們配置的Aspect座咆,生成 Advisors 鏈的?
閑話不多說,讓我們直接開始瞬捕。
獲取對應 Bean 適配的Advisors 鏈
獲取對應 Bean 適配的 Advisors 鏈歉嗓,分為兩步丰介。
- 獲取容器所有的 advisors 作為候選,即解析Spring 容器中所有 Aspect 類中的 advice 方法鉴分,包裝成 advisor哮幢;
- 從候選的 Advisors 中篩選出適配當前 Bean的 Advisors 鏈;
未免讀者閱讀不連貫志珍,我們重新貼一下上篇文章中我們最后講解的一段源碼橙垢,由此繼續(xù)往下講述。
源碼位置:AbstractAutoProxyCreator#wrapIfNecessary(..)
源碼位置:AspectJAwareAdvisorAutoProxyCreator#shouldSkip(..)
源碼位置:AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean(..)
碴裙、AbstractAdvisorAutoProxyCreator#findEligibleAdvisors(..)
可以看到兩個方法都調(diào)用了findCandidateAdvisors()
方法钢悲,也就是去獲取候選的 Advisors,我們進去看看里面干了什么舔株。
1. 獲取候選的 Advisors
從Debug 出來的線程椵毫眨可以看出,AnnotationAwareAspectJAutoProxyCreator
通過 持有 BeanFactoryAspectJAdvisorsBuilder
對象载慈,來獲取Advisor鏈惭等。
再往下看。源碼位置:BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors
內(nèi)容較長办铡,請大家跟著注釋耐心看下去辞做。
這個方法除了等下要講 advisorFactory.getAdvisors(..)
以外,需要注意的就是其為了避免每次都去獲取所有的beanName寡具,解析判斷秤茅,引入了緩存的機制;還有就是Aspect類是根據(jù)Spring Bean 是否被 @Aspect
注解修飾來判斷的童叠。
我們接下去看框喳,真正的去獲取我們的Advisor的方法,this.advisorFactory.getAdvisors(factory)
方法如下:
源碼位置:ReflectiveAspectJAdvisorFactory#getAdvisors(..)
@Override
public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
//獲取我們的標記為Aspect的類
Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
//獲取我們的切面類的名稱
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
//校驗我們的切面類
validate(aspectClass);
// 這里使用了裝飾器模式厦坛,目的是使MetadataAwareAspectInstanceFactory只實例化一次
MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
List<Advisor> advisors = new ArrayList<>();
//獲取到切面類中的所有方法五垮,但是該方法不會解析到標注了@PointCut注解的方法
for (Method method : getAdvisorMethods(aspectClass)) {
// 循環(huán)解析我們切面中的方法
// 獲取當前方法的增強器Advisor
Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
if (advisor != null) {
advisors.add(advisor);
}
}
// 如果尋找的增強器不為空而且又配置了增強延遲初始化那么需要在首位加入同步實例化增強器
if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
advisors.add(0, instantiationAdvisor);
}
// Find introduction fields.
// 獲取 DeclareParents 注解
for (Field field : aspectClass.getDeclaredFields()) {
Advisor advisor = getDeclareParentsAdvisor(field);
if (advisor != null) {
advisors.add(advisor);
}
}
return advisors;
}
解析advice 方法成 advisor對象,
源碼位置:ReflectiveAspectJAdvisorFactory#getAdvisor(..)
@Override
public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) {
//校驗
validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());
//獲取當前方法的切入點 pointcut
AspectJExpressionPointcut expressionPointcut = getPointcut(candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
if (expressionPointcut == null) {
return null;
}
//根據(jù)切點信息生成 advisor
return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod, this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}
生成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時調(diào)用的這個類的構(gòu)造函數(shù)莉撇,
里面包括了一個重要的方法instantiateAdvice,即創(chuàng)建Advice亭姥,這也是我要強調(diào)的重點稼钩,怎么解析出來一個advice。
源碼位置:InstantiationModelAwarePointcutAdvisorImpl#instantiateAdvice(..)
源碼位置:ReflectiveAspectJAdvisorFactory#getAdvice(..)
第一次解析Advisor的時機
關(guān)于第一次解析Advisor的時機达罗,我剛開始也搞混了坝撑。所以在這里說明一下。
這個圖是之前貼過的粮揉,第一次觸發(fā)的截圖巡李。
AnnotationAwareAspectJAutoProxyCreator
繼承了AbstractAutoProxyCreator
實現(xiàn)了InstantiationAwareBeanPostProcessor
接口:
會在生成target class 對象之前,調(diào)用 postProcessBeforeInstantiation(..)
扶认,具體的代碼可以去看AbstractAutowireCapableBeanFactory#createBean(..)
方法侨拦。我們這邊直接看一下 postProcessBeforeInstantiation(..)
在AbstractAutoProxyCreator
中的實現(xiàn)。
/**
* 在創(chuàng)建Bean的流程中還沒調(diào)用構(gòu)造器來實例化Bean的時候進行調(diào)用(實例化前后)
* AOP解析切面在這里完成
*/
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
// 構(gòu)建我們的緩存key
Object cacheKey = getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
// 如果被解析過直接返回
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
// 判斷是不是基礎(chǔ)的Bean(Advice辐宾、PointCut狱从、Advisor、AopInfrastructureBean)是就直接跳過
// 判斷是不是應該跳過 (AOP解析直接解析出我們的切面信息(并且把我們的切面信息進行緩存)叠纹,
// 而事務在這里是不會解析的)
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
return null;
}
2. 篩選 出 適配當前類的 Advisors
這里來一條分割線季研,至此,findCandidateAdvisors()
算是解析完畢了誉察。
但是我們通過這個方法只是獲得了所有候選的advisors与涡,還記得我們這一節(jié)的標題不?
【獲取對應 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(..)
我們接下去看篩選的關(guān)鍵方法``AopUtils#canApply(..)`
篩選的工作主要由 ClassFilter 和 MethodMatcher 完成鸿秆,比如AspectJExpressionPointcut的實現(xiàn)了ClassFilter和MethodMatcher接口酌畜,最終由AspectJ表達式解析,這個地方就復雜了卿叽,也不是核心點檩奠。
又是一條分割線。
到這里之后附帽,Advisor的篩選過程我們算是講完了。
經(jīng)過排序之后井誉,我們算是拿到了這個目標類使用的 Advisors 鏈蕉扮。
現(xiàn)在,讓我們再回到最初的起點颗圣。
源碼位置:AbstractAutoProxyCreator#wrapIfNecessary(..)
小結(jié)
到這里喳钟,大家可以回顧一下屁使,我們總算是把TODO-1
【Spring AOP 如何 獲取對應 Bean 適配的Advisors 鏈】介紹完畢了,總結(jié)一下核心邏輯就是:
- 獲取當前 IoC 容器中所有的 Aspect 類
- 給 每個Aspect 類的advice 方法創(chuàng)建一個 Spring Advisor奔则,這一步又能細分為
- 遍歷所有advice 方法
- 解析方法的注解和pointcut
- 實例化 Advisor 對象
- 獲取到 候選的 Advisors蛮寂,并且緩存起來,方便下一次直接獲取
- 從候選的 Advisors 中篩選出與目標類 適配的Advisor
- 獲取到 Advisor 的 切入點 pointcut
- 獲取到 當前 target 類 所有的 public 方法
- 遍歷方法易茬,通過 切入點 的 methodMatcher 匹配當前方法酬蹋,只有有一個匹配成功就相當于當前的Advisor 適配
- 對篩選之后的 Advisor 鏈進行排序
- 結(jié)束
下一節(jié)中,我們會介紹 【代理類的創(chuàng)建過程】抽莱,我們下次再會范抓。
如果本文有幫助到你,希望能點個贊食铐,這是對我的最大動力匕垫。