關(guān)于 Spring AOP的一些理解

事情要從一次奇怪的 bug 說(shuō)起挫望, 過(guò)程大約是這樣的脊岳, 在一次使用 @Transactional 注解的過(guò)程中御雕, 為了分離真正需要事物的代碼和其他業(yè)務(wù)邏輯膏燃, 我對(duì)代碼進(jìn)行了抽取灌具, 抽取后的代碼類似于下面這樣:

但是在進(jìn)行測(cè)試時(shí)發(fā)現(xiàn)雖然加上了事物注解青团, 但是事物卻完全沒(méi)有生效。 當(dāng)時(shí)可以說(shuō)是百思不得其解咖楣, 只能猜測(cè)是因?yàn)?aop 是通過(guò) jdk 動(dòng)態(tài)代理方式實(shí)現(xiàn)的督笆, 因?yàn)閏ommitSomething 方法并未聲明在接口上, 自然也就不能被代理诱贿, 也就不能實(shí)現(xiàn)事物娃肿。 為驗(yàn)證這個(gè)猜測(cè), 修改 proxyTargetClass=true 切換為 cglib 形式代理珠十, 我們知道料扰, cglib 是通過(guò)子類化來(lái)實(shí)現(xiàn)的代理, 具體生成的代碼類似于下面這樣:

但是修改為 cglib代理方式后事物依然沒(méi)有生效焙蹭。為了弄清楚這個(gè)問(wèn)題晒杈, 我做了一些研究,并在這個(gè)過(guò)程中對(duì) Spring 實(shí)現(xiàn) Aop 的方式做了如下總結(jié):

我們知道 AOP 是Aspect Oriented Programming 面向切面編程的簡(jiǎn)稱孔厉,常被我們用作在事物拯钻, 日志,安全撰豺,緩存等方面粪般。

而 Spring 也實(shí)現(xiàn)了 AOP 機(jī)制,主要有兩種方式 :

1.Proxy base

2.AspectJ

第一種是基于代理方式實(shí)現(xiàn), 比如 JDK Dynamic Proxy 和 Cglib proxy

第二種是基于 AspectJ 框架織入源代碼方式實(shí)現(xiàn)郑趁。

下面我主要講下 Spring Proxy base AOP 方式的實(shí)現(xiàn)刊驴。

代理類構(gòu)成

Spring 默認(rèn)有兩種形式代理 jdk dynamic proxy 和 cglib proxy 在Spring 中這兩種形式代理由 AopProxy 負(fù)責(zé)創(chuàng)建 。AopProxy有兩個(gè)實(shí)現(xiàn)類 JdkDynamicAopProxy 和 CglibAopProxy 繼承關(guān)系如下圖:

選擇代理類

Spring 默認(rèn)使用 jdk dynamic proxy 方式實(shí)現(xiàn) AOP, 當(dāng)被代理類無(wú)接口時(shí) Spring 使用 cglib 方式實(shí)現(xiàn)代理寡润。創(chuàng)建 AopProxy 邏輯主要在類DefaultAopProxyFactory中代碼如下:

可以看到判斷使用哪一種代理實(shí)現(xiàn)邏輯是根據(jù) config.isProxyTargetClass 來(lái)判斷的, 而這個(gè) config 是一個(gè)AdvisedSupport 的實(shí)例舅柜。 AdvisedSupport 是管理Spring AOP 配置的類梭纹, 類層次結(jié)構(gòu)如下:

而 Spring 正是通過(guò) ProxyFactory getProxy 來(lái)構(gòu)造的代理。

ProxyFactory 的 isProxyTargetClass 也會(huì)在真正創(chuàng)建代理對(duì)象時(shí)提前計(jì)算好致份。

創(chuàng)建并調(diào)用ProxyFacotry在類 AnnotationAwareAspectJAutoProxyCreator 中代碼如下:

可以看到 AnnotationAwareAspectJAutoProxyCreator 的 createProxy 主要做了兩件事变抽,

1.創(chuàng)建并初始化 ProxyFactory

2.創(chuàng)建代理 bean

代理類的調(diào)用與執(zhí)行

由于 JdkDynamicAopProxy是基于 Java的動(dòng)態(tài)代理,相信大家都比較熟悉, 這里就略過(guò)不講绍载。后文主要講下 CglibAopProxy 調(diào)用與執(zhí)行诡宗。

在前文中可以看到 Spring 是通過(guò) AnnotationAwareAspectJAutoProxyCreator的createProxy方法來(lái)構(gòu)造具有AOP能力的 bean, 這個(gè)過(guò)程就是設(shè)置好代理的攔截器以及一些其他與構(gòu)造代理相關(guān)屬性后將創(chuàng)建過(guò)程委托給 JdkDynamicAopProxy 或 CglibAopProxy 中具體一個(gè)击儡, 再由 JdkDynamicAopProxy 或 CglibAopProxy 調(diào)用 getProxy 創(chuàng)建代理 bean塔沃。 所以實(shí)際我們通過(guò) @Autowired 注解或者從 Spring 上下文中拿到的 bean 就已經(jīng)是生成好的代理 bean了。 可以 debug 看下阳谍, 獲取的 bean 的 class 一般是 $Proxy 或 $EnhanceByCGLIB 這樣的后綴蛀柴。

繼續(xù)看下代理攔截邏輯也就是 AOP 的切面邏輯是如何實(shí)現(xiàn)的, 在 CglibAopProxy 構(gòu)造代理對(duì)象時(shí)代理攔截邏輯是通過(guò)如下代碼實(shí)現(xiàn)注冊(cè):

而 Spring 主要是通過(guò) DynamicAdvisedInterceptor 類實(shí)現(xiàn)攔矫夯,截繼承關(guān)系如圖:

DynamicAdvisedInterceptor 的 intercept方法實(shí)現(xiàn)了具體的攔截邏輯:

可以看到具體實(shí)現(xiàn)邏輯首先是獲取攔截器鏈鸽疾, 再通過(guò) CglibMethodInvocation 來(lái)執(zhí)行攔截器鏈, 具體再看 CglibMethodInvocation 的 invokeJoinpoint 方法, 具體邏輯如下:

Spring 通過(guò)攔截器鏈對(duì)被調(diào)用方法進(jìn)行動(dòng)態(tài)匹配如果匹配上則進(jìn)行連接器調(diào)用训貌,否則繼續(xù)往后面鏈路執(zhí)行制肮,當(dāng)所有攔截器都被執(zhí)行后調(diào)用

methodProxy.invoke(target, arguments)

執(zhí)行被代理方法。 至此 Spring Proxy base 代理整個(gè)流程基本梳理完畢递沪。

總結(jié)

理解完spring 整個(gè) aop 實(shí)現(xiàn)邏輯也就可以理解前文 bug, 其實(shí)前文中不能應(yīng)用上事物問(wèn)題豺鼻,主要原因是被代理方法的內(nèi)部方法調(diào)用引起的。 因?yàn)樵诜椒▋?nèi)部区拳, 作用域就不是 proxy 對(duì)象拘领, 而是 target source (被代理源對(duì)象), 所以理解了原因解決這個(gè)問(wèn)題自然也就簡(jiǎn)單了:

主要可以通過(guò)

1.AopContext.currentProxy() 獲取代理對(duì)象

2.從Spring上下文中獲取代理對(duì)象

3.使用 AspectJ 方式實(shí)現(xiàn) Aop

本文作者:馮劍濤(點(diǎn)融黑幫)樱调,主要從事Java 后端開(kāi)發(fā)工作约素,平時(shí)喜歡研究技術(shù),愛(ài)好是旅行和跑步笆凌。 目前主要負(fù)責(zé) crc & ams 系統(tǒng)圣猎。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市乞而,隨后出現(xiàn)的幾起案子送悔,更是在濱河造成了極大的恐慌,老刑警劉巖爪模,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件欠啤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡屋灌,警方通過(guò)查閱死者的電腦和手機(jī)洁段,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)共郭,“玉大人祠丝,你說(shuō)我怎么就攤上這事疾呻。” “怎么了写半?”我有些...
    開(kāi)封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵岸蜗,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我叠蝇,道長(zhǎng)璃岳,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任蟆肆,我火速辦了婚禮矾睦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘炎功。我一直安慰自己枚冗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布蛇损。 她就那樣靜靜地躺著赁温,像睡著了一般。 火紅的嫁衣襯著肌膚如雪淤齐。 梳的紋絲不亂的頭發(fā)上股囊,一...
    開(kāi)封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音更啄,去河邊找鬼稚疹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛祭务,可吹牛的內(nèi)容都是我干的内狗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼义锥,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼柳沙!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起拌倍,我...
    開(kāi)封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤赂鲤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后柱恤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體数初,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年梗顺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妙真。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荚守,死狀恐怖珍德,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情矗漾,我是刑警寧澤锈候,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站敞贡,受9級(jí)特大地震影響泵琳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜誊役,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一获列、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛔垢,春花似錦击孩、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至艺玲,卻和暖如春括蝠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饭聚。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工忌警, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秒梳。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓法绵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親端幼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子礼烈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • 事情要從一次奇怪的 bug 說(shuō)起, 過(guò)程大約是這樣的婆跑, 在一次使用 @Transactional 注解的過(guò)程中此熬, ...
    FinleyFeng閱讀 970評(píng)論 0 0
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)滑进,斷路器犀忱,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • 什么是Spring Spring是一個(gè)開(kāi)源的Java EE開(kāi)發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,441評(píng)論 1 133
  • title: Spring_AOP源碼分析date: 2016-11-03 01:15:11categories:...
    raincoffee閱讀 1,731評(píng)論 2 36
  • 文章作者:Tyan博客:noahsnail.com 3.4 Dependencies A typical ente...
    SnailTyan閱讀 4,128評(píng)論 2 7