【Spring源碼】20.AOP之切面的收集

image

1. 前言

我們知道aop實(shí)現(xiàn)的原理肯定是基于jdk動(dòng)態(tài)代理和cglib代理净刮,經(jīng)過生成代理對(duì)象琼腔,對(duì)命中切面的方法進(jìn)行增強(qiáng)更振。并將代理對(duì)象存放到ioc容器中,在其他對(duì)象需要的時(shí)候注入進(jìn)去, 那么肯定 需要通過解析類 去解析 并收集 Advisor對(duì)象集合 : 需要增強(qiáng)的目標(biāo)PointCut 以及 具體增強(qiáng)的邏輯Advice丧凤。 如果這個(gè)類是可以找到增強(qiáng)對(duì)象的,那么就應(yīng)該生成代理步脓。

2. 問題

2.1 生成的代理對(duì)象需要什么愿待?

首先 需要進(jìn)行 切面的收集和匹配

然后 把匹配到的切面 放入 生成的代理對(duì)象里,以便后續(xù)調(diào)用代理方法 來 決定調(diào)用哪個(gè)切面 來增強(qiáng)沪编。

我們來看下wrapIfNecessary方法里是如何 進(jìn)行切面的收集和匹配的呼盆。

image

3. Advisor增強(qiáng)對(duì)象的收集和匹配

3.1 Advisor增強(qiáng)對(duì)象的收集

該方法會(huì)先收集適合所有的Advisor增強(qiáng)對(duì)象年扩,并過濾出匹配當(dāng)前bean的Advisor對(duì)象

image

getAdvicesAndAdvisorsForBean

image

收集及匹配全在這

image

先看收集所有的切面findCandidateAdvisors()

這個(gè)方法是被他的子類重寫并調(diào)用的蚁廓,看子類AnnotationAwareAspectJAutoProxyCreator的該方法

image
image

spring自帶的advisors可以不看,我們直接看我們自己寫的@Aspect類

image

這里有兩個(gè)緩存

image

buildAspectJAdvisors方法里會(huì)先判斷緩存

image

第一次沒有厨幻,肯定先解析再獲取相嵌,我們看解析的邏輯

可以看到會(huì)遍歷spring容器里的每一個(gè)beanName,然后根據(jù)beanName獲取class,如果類上有Aspect注解,說明是需要解析的

將 beanFactory, beanName 封裝成一個(gè)獲取Advisor的工廠况脆,然后調(diào)用 this.advisorFactory.getAdvisors(factory) 去獲取advisors集合

image

3.1.1. 獲取沒有 @PointCut注解的方法集合

取出從獲取advisors工廠里取出beanClass, 去遍歷所有沒有@PointCut注解的方法饭宾,包含什么注解也沒有的方法

image

具體怎么獲取,也很簡(jiǎn)單格了,遍歷每一個(gè)方法看铆,如果沒有@Pointcut 注解就可以

image

3.1.2. 對(duì)要解析的方法集合排序

隨后會(huì)對(duì) 沒有@PointCut注解的方法集合進(jìn)行排序, 按照這個(gè)順序排序 : Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class

image

METHOD_COMPARATOR 比較器 :

image

3.1.3. 遍歷沒有@PointCut注解的方法的集合盛末,開始創(chuàng)建Advisor對(duì)象

這里就開始要根據(jù)方法上面 aop相關(guān)的注解 來常見Advisor對(duì)象弹惦。

首先需要明確一點(diǎn),Advisor定義的 是 增強(qiáng)的目標(biāo) 與 增強(qiáng)的邏輯悄但,

  1. 增強(qiáng)的目標(biāo) 則是Pointcut對(duì)象定義的
  2. 增強(qiáng)的邏輯 則是Advice對(duì)象定義的

所以棠隐,Advisor 必須要包含Pointcut對(duì)象和 Advice對(duì)象。

image

3.1.3.1. Pointcut接口介紹

基于表達(dá)式的PointCut實(shí)現(xiàn)類是 AspectJExpressionPointcut類檐嚣,它除了多持有一個(gè)表達(dá)式外助泽,還會(huì)實(shí)現(xiàn)的 Pointcut接口。

 public interface Pointcut {

   ClassFilter getClassFilter();

   MethodMatcher getMethodMatcher();

   Pointcut TRUE = TruePointcut.INSTANCE;
}

需要重寫getClassFilter()來提供ClassFilter, ClassFilter是一個(gè)接口嚎京,重寫matches方法嗡贺,來定義是否與類匹配

@FunctionalInterface
public interface ClassFilter {
        // 定義是否與類匹配
   boolean matches(Class<?> clazz);

   ClassFilter TRUE = TrueClassFilter.INSTANCE;
}

重寫 getMethodMatcher()方法 來提供MethodMatcher 對(duì)象, MethodMatcher也是一個(gè)接口

public interface MethodMatcher {
  // 定義是否與類匹配
   boolean matches(Method method, Class<?> targetClass);

   boolean isRuntime();
     // 定義是否與方法匹配
   boolean matches(Method method, Class<?> targetClass, Object... args);

   MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

這里AspectJExpressionPointcut類鞍帝,自己實(shí)現(xiàn)了ClassFilter接口诫睬,和IntroductionAwareMethodMatcher接口,可以看到實(shí)現(xiàn)至Pointcut接口接口的兩個(gè)方法 返回的都是自身膜眠。

image

重寫自ClassFilter接口岩臣,和IntroductionAwareMethodMatcher接口 對(duì)應(yīng)的matches方法溜嗜。
image

這里先這樣,后面匹配的時(shí)候 再看這兩個(gè)方法里具體的匹配邏輯架谎。

3.1.3.2. PointCut對(duì)象的創(chuàng)建

進(jìn)入 getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) 方法

image

進(jìn)入 getPointcut方法炸宵,這里先獲取Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 這幾個(gè)注解,匹配到就返回

不可能獲取到@Pointcut注解
其實(shí)這里是不可能獲取到 @Pointcut ,因?yàn)榍懊?遍歷的整個(gè)方法的集合 就已經(jīng)是排除掉了 @Pointcut注解的方法

獲取需要遍歷的整個(gè)方法集合代碼                 ![image](https://upload-images.jianshu.io/upload_images/23353704-e4ef94bec8d68a4a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

getPointcut方法

注解獲取到Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class注解里的其中任意一個(gè)谷扣,獲取注解里的pointcutExpression屬性土全,以及目標(biāo)類的class對(duì)象,設(shè)置到創(chuàng)建的AspectJExpressionPointcut對(duì)象里会涎,并返回

image

到這里pointCut對(duì)象已經(jīng)創(chuàng)建完成裹匙,其實(shí)并不會(huì)去 掃描有@PointCut注解的方法,只是通過Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class里的pointcut屬性末秃,去指向@PointCut注解的方法概页,到時(shí)候可以 根據(jù)這個(gè) 去找 @PointCut注解的方法,再去找@PointCut里面具體配置的表達(dá)式,明確切點(diǎn)练慕。

3.1.3.3. Advice對(duì)象的創(chuàng)建

代碼回到入 getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
int declarationOrderInAspect, String aspectName) 方法惰匙。

會(huì)new出InstantiationModelAwarePointcutAdvisorImpl對(duì)象, 傳入AspectJExpressionPointcut對(duì)象到構(gòu)造方法里

image

InstantiationModelAwarePointcutAdvisorImpl構(gòu)造方法

會(huì)先存PointCut對(duì)象以及原生Method對(duì)象,再在instantiateAdvice方法里創(chuàng)建Advice對(duì)象

image

instantiateAdvice方法里創(chuàng)建Advice對(duì)象

image

首先從原生method方法铃将,取出 Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class 這五個(gè)注解中的一個(gè)(一個(gè)方法只能取到其中一個(gè)注解项鬼,一般只會(huì)配一個(gè))

image

再判斷注解,根據(jù)注解的不同劲阎,創(chuàng)建的Advice對(duì)象也不同

image

可以看到注解與創(chuàng)建的AspectJAroundAdvice類有如下對(duì)應(yīng)關(guān)系

注解 Aavice實(shí)現(xiàn)類
@Around AspectJAroundAdvice
@Before AspectJMethodBeforeAdvice
@After AspectJAfterAdvice
@AfterReturning AspectJAfterReturningAdvice
@AfterThrowing AspectJAfterThrowingAdvice
MethodInterceptor接口

其中AspectJAroundAdvice,AspectJMethodBeforeAdvice,AspectJAfterAdvice這三個(gè)類是實(shí)現(xiàn)MethodInterceptor接口的

實(shí)現(xiàn)的invoke方法 持有 整個(gè)aop切面調(diào)用鏈上下文對(duì)象绘盟,以完成 切面調(diào)用鏈的回調(diào),繼續(xù)調(diào)用剩下的的 切面悯仙。

@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
   Object invoke(MethodInvocation invocation) throws Throwable;
}
public class AspectJAfterAdvice extends AbstractAspectJAdvice
      implements MethodInterceptor, AfterAdvice, Serializable {
   @Override
   public Object invoke(MethodInvocation mi) throws Throwable {
      try {
         return mi.proceed();
      }
      finally {
         invokeAdviceMethod(getJoinPointMatch(), null, null);
      }
   }
}
public class AspectJMethodBeforeAdvice extends AbstractAspectJAdvice implements MethodBeforeAdvice, Serializable {
    public Object invoke(){
    // 繼承的父類 AbstractAspectJAdvice的invoke
    }
    @Override
    public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
}

public class AspectJAfterAdvice extends AbstractAspectJAdvice
        implements MethodInterceptor, AfterAdvice, Serializable {
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        try {
            return mi.proceed();
        }
        finally {
            invokeAdviceMethod(getJoinPointMatch(), null, null);
        }
    }
}

而AspectJAfterReturningAdvice,AspectJAfterThrowingAdvice這兩個(gè)切面沒有龄毡,到時(shí)候aop調(diào)用的時(shí)候,會(huì)對(duì)這兩個(gè)切面類進(jìn)行 MethodInterceptor類型適配雁比,以保證 調(diào)用和調(diào)用鏈 傳遞邏輯的 統(tǒng)一稚虎。

最終返回 各自的切面對(duì)象。

image

Advisor對(duì)象創(chuàng)建完成偎捎,存入緩存

直到所有方法都掃描完成蠢终,對(duì)應(yīng)的Advisor對(duì)象 也創(chuàng)建成功,都存入緩存: beanName -> 對(duì)應(yīng)類解析出來的 advisors

image

至此, 當(dāng)容器內(nèi)所有的beanName都遍歷完成茴她,對(duì)應(yīng)的class 通過解析寻拂、創(chuàng)建出來的Advisor對(duì)象全部 收集完畢。

image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末丈牢,一起剝皮案震驚了整個(gè)濱河市祭钉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌己沛,老刑警劉巖慌核,帶你破解...
    沈念sama閱讀 221,273評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件距境,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡垮卓,警方通過查閱死者的電腦和手機(jī)垫桂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粟按,“玉大人诬滩,你說我怎么就攤上這事∶鸾” “怎么了疼鸟?”我有些...
    開封第一講書人閱讀 167,709評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)庙曙。 經(jīng)常有香客問我空镜,道長(zhǎng),這世上最難降的妖魔是什么矾利? 我笑而不...
    開封第一講書人閱讀 59,520評(píng)論 1 296
  • 正文 為了忘掉前任姑裂,我火速辦了婚禮馋袜,結(jié)果婚禮上男旗,老公的妹妹穿的比我還像新娘。我一直安慰自己欣鳖,他們只是感情好察皇,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,515評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著泽台,像睡著了一般什荣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上怀酷,一...
    開封第一講書人閱讀 52,158評(píng)論 1 308
  • 那天稻爬,我揣著相機(jī)與錄音,去河邊找鬼蜕依。 笑死桅锄,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的样眠。 我是一名探鬼主播友瘤,決...
    沈念sama閱讀 40,755評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼檐束!你這毒婦竟也來了辫秧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,660評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤被丧,失蹤者是張志新(化名)和其女友劉穎盟戏,沒想到半個(gè)月后绪妹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡柿究,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,287評(píng)論 3 340
  • 正文 我和宋清朗相戀三年喂急,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笛求。...
    茶點(diǎn)故事閱讀 40,427評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡廊移,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出探入,到底是詐尸還是另有隱情狡孔,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評(píng)論 5 349
  • 正文 年R本政府宣布蜂嗽,位于F島的核電站苗膝,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏植旧。R本人自食惡果不足惜辱揭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,801評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望病附。 院中可真熱鬧问窃,春花似錦、人聲如沸完沪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽覆积。三九已至听皿,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宽档,已是汗流浹背尉姨。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留吗冤,地道東北人又厉。 一個(gè)月前我還...
    沈念sama閱讀 48,808評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像欣孤,于是被迫代替她去往敵國(guó)和親馋没。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,440評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容