Spring源碼分析之 AOP(一湃密,創(chuàng)建代理對(duì)象)

一诅挑,@EnableAspectJAutoProxy開啟AspectJ自動(dòng)代理

在這個(gè)注解中導(dǎo)入了一個(gè)@Import(AspectJAutoProxyRegistrar.class)類。這個(gè)類實(shí)現(xiàn)了ImportBeanDefinitionRegistrar(Bean定義的注冊(cè)器)泛源,AspectJAutoProxyRegistrar實(shí)現(xiàn)了一個(gè)registerBeanDefinitions方法拔妥。通過AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);去獲取名為AUTO_PROXY_CREATOR_BEAN_NAME="org.springframework.aop.config.internalAutoProxyCreator"的Bean定義信息;然后幫我們導(dǎo)入AnnotationAwareAspectJAutoProxyCreator這個(gè)組件

二,AnnotationAwareAspectJAutoProxyCreator展開調(diào)查

1达箍,如圖可知没龙,AnnotationAwareAspectJAutoProxyCreator這個(gè)類同時(shí)具有BeanFactory和BeanPostProcessor屬性。點(diǎn)進(jìn)去這個(gè)類,看看他們是怎么實(shí)現(xiàn)的硬纤,點(diǎn)進(jìn)去發(fā)現(xiàn)AnnotationAwareAspectJAutoProxyCreator這個(gè)類本身并沒有什么我們想要的解滓,但是他有父類。一直點(diǎn)筝家,找到最后一層父類AbstractAutoProxyCreator洼裤。

2通過查看,最終找到如下關(guān)系溪王,由于AnnotationAwareAspectJAutoProxyCreator是AbstractAutoProxyCreator的子類腮鞍,所以就找到AnnotationAwareAspectJAutoProxyCreator和BeanPostProcessor的關(guān)系,實(shí)現(xiàn)了一個(gè)后置處理器的前置方法莹菱。然后再找AnnotationAwareAspectJAutoProxyCreator和BeanFactroyAware的關(guān)系移国。AbstractAutoProxyCreator實(shí)現(xiàn)了postProcessBeforeInstantiation接口而postProcessBeforeInitialization是開放式接口。用于開發(fā)者自己實(shí)現(xiàn)

3道伟,通過下面圖可知迹缀,找到他們之間的關(guān)系

4,下面給出一張完整的關(guān)系圖蜜徽。

5祝懂,接下來就圍繞這張關(guān)系圖去找主要的類,找主要的方法去理清楚AOP的實(shí)現(xiàn)過程拘鞋。由上圖可知嫂易,一個(gè)最主要的類就是AbstractAutoProxyCreator,還有最主要的兩個(gè)方法也就是那個(gè)后置處理器的前置方法和后置方法掐禁。postProcessBeforeInstantiation、postProcessAfterInitialization颅和、下面一個(gè)一個(gè)進(jìn)行分析傅事。


三,postProcessBeforeInstantiation

一峡扩,找事務(wù)的增強(qiáng)器蹭越。

還記得之前的IOC的那篇文章在執(zhí)行doCreateBean之前,resolveBeforeInstantiation這個(gè)方法將Bean定義信息進(jìn)行了代理教届。并且放入了緩存中响鹃。postProcessBeforeInstantiation這個(gè)方法就是從resolveBeforeInstantiation這里方法里面走進(jìn)來的。點(diǎn)進(jìn)postProcessBeforeInstantiation這個(gè)方法案训,Object cacheKey = getCacheKey(beanClass, beanName);進(jìn)來就去從緩存拿买置,然后看isInfrastructureClass這個(gè)方法,判斷的是是不是基礎(chǔ)的Bean强霎。什么是基礎(chǔ)Bean比如Advice.class\Pointcut.class\Advisor.class等等等忿项。然后還要判斷shouldSkip()這個(gè)方法應(yīng)不應(yīng)該跳過。點(diǎn)進(jìn)去我們發(fā)現(xiàn)有一個(gè)這樣的findCandidateAdvisors()方法,這個(gè)方法就是在找事務(wù)的增強(qiáng)器轩触。那么為什么這么說寞酿,因?yàn)樵诟割惱锿ㄟ^advisorRetrievalHelper(切面查找工具類)這個(gè)類的findAdvisorBeans方法可知并沒有找到實(shí)現(xiàn)了Advisor.class的類。從上圖也能看出AnnotationAwareAspectJAutoProxyCreator的類圖里面也沒有看到跟Advisor.class相關(guān)的類或者實(shí)現(xiàn)脱柱。這個(gè)方法是怎么判斷的呢?它進(jìn)來先是從緩存里面去拿伐弹,沒拿到,緊接著去容器當(dāng)中去找榨为。BeanFactoryUtils.beanNamesForTypeIncludingAncestors惨好,也沒找到,所以直接返回了一個(gè)空的List對(duì)象柠逞。由此可知昧狮,我們可以把findCandidateAdvisors()方法理解為在查找事務(wù)的增強(qiáng)器。下面給出一張流程圖便于理解板壮。

二逗鸣,尋找我們自己定義的AspectJ

findCandidateAdvisors()這個(gè)方法走完以后,沒找到關(guān)于事務(wù)的切面绰精,然后接著往下走this.aspectJAdvisorsBuilder.buildAspectJAdvisors()這段代碼點(diǎn)進(jìn)去撒璧,進(jìn)去發(fā)現(xiàn)第一次this.aspectBeanNames獲取依然是null,然后如果為空的話笨使,通過BeanFactoryUtils.beanNamesForTypeIncludingAncestors這個(gè)東西卿樱,把所有的類的名稱都撈出來。然后通過for循環(huán)你的BeanName去獲取你的BeanType硫椰,然后判斷你的beanType是不是AspectJ繁调。如果 是接著往下走找到getAdvisors方法。第一步獲取增強(qiáng)器的類型getAspectMetadata().getAspectClass()靶草。 在獲取增強(qiáng)器的名稱蹄胰。再往下看,有一個(gè)for循環(huán)奕翔,里面有一個(gè)getAdvisorMethods方法裕寨,也就是去循環(huán)你的這個(gè)方法,點(diǎn)到這個(gè)方法里面看一看派继,然后會(huì)發(fā)現(xiàn)宾袜,通過這個(gè)doWithMethods方法,會(huì)把你所有的增強(qiáng)器撈出來驾窟。并且要排除你的切點(diǎn)(Pointcut.class)庆猫。

三, 獲取增強(qiáng)器getAdvisors

1首先循環(huán)你所有去除切點(diǎn)的增強(qiáng)器方法。沒循環(huán)一次調(diào)用一次getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName)方法绅络。點(diǎn)進(jìn)getAdvisor方法阅悍。接下來找到一個(gè)創(chuàng)建切面的實(shí)現(xiàn)類InstantiationModelAwarePointcutAdvisorImpl(實(shí)例化切面)好渠,點(diǎn)進(jìn)去,這個(gè)方法里前面做了一些基礎(chǔ)賦值和判斷是否是懶加載等校驗(yàn)节视。下面有個(gè)最關(guān)鍵的方法instantiateAdvice(this.declaredPointcut);點(diǎn)進(jìn)去找到getAdvice方法(獲取通知)拳锚。在這個(gè)方法中找到AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod)這個(gè)方法,他是意思是說寻行,在方法上找到所有的切面上的注解霍掺。這個(gè)方法里會(huì)遍歷如下圖所示的類。如果找到了直接返回到getAdvice這個(gè)方法拌蜘。

2杆烁,回到getAdvice方法中可以看到下面有一個(gè)switch選擇。如下圖简卧,通過這switch判斷你或取到的注解的類型是什么東東兔魂。如果是before就new一個(gè)AspectJMethodBeforeAdvice,這個(gè)東西就是切面通知举娩。到這里直接就返回到getAdvisors方法中把它放入advisors中去析校。下面在畫一張流程圖

3,流程圖

3铜涉,最終執(zhí)行完智玻,在buildAspectJAdvisors()方法中把剛才找到的切面都放入切面的緩存中去this.advisorsCache.put(beanName, classAdvisors)和this.aspectFactoryCache.put(beanName, factory);放到這兩個(gè)緩存里面來。至此shouldSkip()方法執(zhí)行完畢芙代。最后發(fā)現(xiàn)?postProcessBeforeInstantiation這個(gè)東西他就是說將我們自己定義切面都給撈出來吊奢。并且放入增強(qiáng)器緩存中。


四:postProcessAfterInitialization創(chuàng)建代理對(duì)象纹烹。

點(diǎn)進(jìn)去這個(gè)方法页滚,也許有人會(huì)問這個(gè)方法是從哪里進(jìn)來的。在分析IOC的時(shí)候铺呵,說到了創(chuàng)建屬性的那個(gè)方法逻谦,在后面進(jìn)行初始化的時(shí)候,執(zhí)行了一些后置處理器postProcessAfterInitialization 這個(gè)方法就是從invokeInitMethods方法中的applyBeanPostProcessorsAfterInitialization方法進(jìn)來的陪蜻。

點(diǎn)進(jìn)去wrapIfNecessary這個(gè)方法前面執(zhí)行了一些判斷找到getAdvicesAndAdvisorsForBean這個(gè)方法。找Advices類型的和Advisors這個(gè)類型贱鼻,這個(gè)方法是找事務(wù)和AOP都是通過這個(gè)方法宴卖。因?yàn)槭聞?wù)和AOP的執(zhí)行流程很相似,事務(wù)的流程在后面的文章會(huì)進(jìn)行講解邻悬。那么這個(gè)方法到底做了什么呢症昏。看名知意父丰。點(diǎn)進(jìn)去發(fā)現(xiàn)了findEligibleAdvisors這個(gè)方法肝谭,找到一個(gè)合適的增強(qiáng)器掘宪。再點(diǎn)進(jìn)去∪林颍看第一行findCandidateAdvisors()又執(zhí)行了一次魏滚,所以里面流程不會(huì)走,而是直接從緩存中拿到了增強(qiáng)器坟漱。因?yàn)樵赽efore中我們創(chuàng)建過了鼠次。既然找到了那么應(yīng)該怎么辦呢,看下面的方法

List eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

這個(gè)意思就是說找到我本類能用的芋齿。比如說你現(xiàn)在有兩個(gè)切面腥寇,當(dāng)本類是A的時(shí)候,那么只能有其中的一個(gè)切面對(duì)我進(jìn)行增強(qiáng)的植入觅捆,那么另一個(gè)可能在切另外一個(gè)類赦役,所以這個(gè)方法就是找出我奔雷能用的切面。點(diǎn)進(jìn)去看看栅炒。然后找到方法canApply(candidate, clazz, hasIntroductions)掂摔,意思是能用的。順便強(qiáng)調(diào)一下职辅,我們只看有用的代碼邊邊角角這里不做介紹棒呛。點(diǎn)進(jìn)canApply方法。在其中你會(huì)發(fā)現(xiàn)有一段advisor? ?instanceof PointcutAdvisor域携,PointcutAdvisor pca = (PointcutAdvisor) advisor;將獲取到的進(jìn)行轉(zhuǎn)換一下看看是否能用簇秒。在點(diǎn)進(jìn)去canApply,這里面是一個(gè)方法的匹配器秀鞭。通過循環(huán)去匹配看看我們能不能用趋观。最后執(zhí)行完,直接返回到getAdvicesAndAdvisorsForBean這個(gè)方法中锋边,這個(gè)方法會(huì)判斷皱坛,如果找到空的就不需要代理。如果找到了下一步就通過wrapIfNecessary這個(gè)方法中的createProxy來進(jìn)行代理豆巨。

2剩辟,createProxy

AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)this.beanFactory, beanName, beanClass);暴露代理對(duì)象

意思就是:@EnableAspectJAutoProxy(exposeProxy =true)暴露代理對(duì)象

點(diǎn)進(jìn)createProxy方法,找到buildAdvisors方法點(diǎn)進(jìn)去,把返回的切面數(shù)組放入proxyFactory.最后找到getProxy這個(gè)方法點(diǎn)進(jìn)去,其實(shí)這個(gè)方法就是真正的去代理的對(duì)象.找到createAopProxy這個(gè)方法.其中一段targetClass.isInterface() || Proxy.isProxyClass(targetClass)如果是接口,或者Proxy的類就用jdk去代理,否則用Cglib去代理或者強(qiáng)制指定Cglib去代理,否則就用jdk去代理.執(zhí)行完返回來,找到getProxy方法,Proxy.newProxyInstance(classLoader, proxiedInterfaces,this);真正的去創(chuàng)建代理對(duì)象.

五,總結(jié)

創(chuàng)建AOP的代理對(duì)象主要就是如下幾點(diǎn)流程,第一postProcessBeforeInstantiation這個(gè)后置處理器就是找到你所有的切面信息.并且放入緩存中去,postProcessAfterInitialization這個(gè)后置處理器就是創(chuàng)建我們的代理對(duì)象.其中的一些校驗(yàn),細(xì)節(jié),上面也有部分的講解.AOP(創(chuàng)建代理對(duì)象)至此結(jié)束,下面附上一張整體的流程圖.


上圖是一張從尋找切面到代理切面的一個(gè)完整的流程圖往扔。

本節(jié)完...如有問題或者錯(cuò)誤,請(qǐng)?jiān)谙路搅粞?歡迎一起探討和經(jīng)驗(yàn)分享!!!

QQ:527324958? ??

微信:xikuang925661

作者:夕陽

java高級(jí)程序員技術(shù)交流群:加群時(shí)請(qǐng)備注加群即可

手寫代不易贩猎。請(qǐng)加關(guān)注,謝謝萍膛。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吭服,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蝗罗,更是在濱河造成了極大的恐慌艇棕,老刑警劉巖蝌戒,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異沼琉,居然都是意外死亡北苟,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門刺桃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來粹淋,“玉大人,你說我怎么就攤上這事瑟慈√乙疲” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵葛碧,是天一觀的道長(zhǎng)借杰。 經(jīng)常有香客問我,道長(zhǎng)进泼,這世上最難降的妖魔是什么蔗衡? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮乳绕,結(jié)果婚禮上绞惦,老公的妹妹穿的比我還像新娘。我一直安慰自己洋措,他們只是感情好济蝉,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著菠发,像睡著了一般王滤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上滓鸠,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天雁乡,我揣著相機(jī)與錄音,去河邊找鬼糜俗。 笑死踱稍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悠抹。 我是一名探鬼主播珠月,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼锌钮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起引矩,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤梁丘,失蹤者是張志新(化名)和其女友劉穎侵浸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體氛谜,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡掏觉,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了值漫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片澳腹。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖杨何,靈堂內(nèi)的尸體忽然破棺而出酱塔,到底是詐尸還是另有隱情,我是刑警寧澤危虱,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布羊娃,位于F島的核電站,受9級(jí)特大地震影響埃跷,放射性物質(zhì)發(fā)生泄漏蕊玷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一弥雹、第九天 我趴在偏房一處隱蔽的房頂上張望垃帅。 院中可真熱鬧,春花似錦剪勿、人聲如沸贸诚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赦颇。三九已至,卻和暖如春赴涵,著一層夾襖步出監(jiān)牢的瞬間媒怯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工髓窜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留扇苞,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓寄纵,卻偏偏與公主長(zhǎng)得像鳖敷,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子程拭,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

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