《Spring設(shè)計(jì)思想》AOP設(shè)計(jì)思想與原理(圖文并茂)

  • 1. Java程序運(yùn)行在JVM中的特征
  • 2. Java程序執(zhí)行流 【了解AOP、連接點(diǎn)(Join Point)掌眠、切入點(diǎn)(point cut) 的概念 】
  • 3. 引入了代理模式的Java程序執(zhí)行流(AOP實(shí)現(xiàn)的機(jī)制)
  • 4. Spring AOP的工作原理

Spring 提供了AOP(Aspect Oriented Programming) 的支持, 那么,什么是AOP呢?本文將通過一個另外一個角度來詮釋AOP的概念,幫助你更好地理解和使用Spring AOP采够。

讀完本文,你將了解到:

1. Java程序運(yùn)行在JVM中的特征

2. Java程序的執(zhí)行流【了解AOP冰垄、連接點(diǎn)(Join Point)蹬癌、切入點(diǎn)(point cut) 的概念 】

3. 引入了代理模式的Java程序執(zhí)行流(AOP實(shí)現(xiàn)的機(jī)制)

4. Spring AOP的工作原理

1. Java程序運(yùn)行在JVM中的特征

當(dāng)我們在某個類Foo中寫好了一個main()方法,然后執(zhí)行java Foo播演,你的Java程序之旅就開啟了冀瓦,如下:

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

那么在這個執(zhí)行的過程中伴奥,JVM都為你干了什么呢写烤?

當(dāng)你執(zhí)行java Foo 的時候,JVM會創(chuàng)建一個主線程main拾徙,這個主線程以上述的main()方法作為入口洲炊,開始執(zhí)行你的代碼。每一個線程在內(nèi)存中都會維護(hù)一個屬于自己的棧(Stack),記錄著整個程序執(zhí)行的過程尼啡。棧里的每一個元素稱為棧幀(Stack Frame)暂衡,棧幀表示著某個方法調(diào)用,會記錄方法調(diào)用的信息崖瞭;實(shí)際上我們在代碼中調(diào)用一個方法的時候狂巢,在內(nèi)存中就對應(yīng)著一個棧幀的入棧和出棧。

在某個特定的時間點(diǎn)书聚,一個Main線程內(nèi)的棧會呈現(xiàn)如下圖所示的情況:

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

從線程棧的角度來看唧领,我們可以看到藻雌,JVM處理Java程序的基本單位是方法調(diào)用。實(shí)際上斩个,JVM執(zhí)行的最基本單位的指令(即原子操作)是匯編語言性質(zhì)的機(jī)器字節(jié)碼胯杭。這里之所以講方法調(diào)用時Java程序的基本執(zhí)行單位,是從更宏觀的角度看待的受啥。

如何獲取到虛擬機(jī)線程棧中的內(nèi)容(即方法調(diào)用過程)做个?

試想一下,如何能夠獲取到JVM線程棧中的方法調(diào)用的內(nèi)容滚局? 我相信所有的Java programmer都知道這個答案居暖。Java Programmer幾乎每天都能看到它------當(dāng)我們的代碼拋出異常而未捕獲或者運(yùn)行時出現(xiàn)了Error錯誤時,我們會受到一個非常討厭的Log信息核畴,如下:

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

當(dāng)然膝但,除了代碼拋出異常外,我們還是可以其他方式察覺JVM線程棧內(nèi)的內(nèi)容谤草「可以通過Thread.dumpStack()方法創(chuàng)建一個假的Exception實(shí)例,然后將這個Exception實(shí)例記錄的當(dāng)前線程棧的內(nèi)容輸出到標(biāo)準(zhǔn)錯誤流中丑孩。例如我在某處代碼里執(zhí)行了Thread.dumpStack()方法冀宴,輸出了如下的結(jié)果:

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

2. Java程序執(zhí)行流 【了解AOP、連接點(diǎn)(Join Point)温学、切入點(diǎn)(point cut) 的概念 】

如果從虛擬機(jī)線程棧的角度考慮Java程序執(zhí)行的話略贮,那么,你會發(fā)現(xiàn)仗岖,真?zhèn)€程序運(yùn)行的過程就是方法調(diào)用的過程逃延。我們按照方法執(zhí)行的順序,將方法調(diào)用排成一串轧拄,這樣就構(gòu)成了Java程序流揽祥。

我們將上述的線程棧里的方法調(diào)用按照執(zhí)行流排列,會有如下類似的圖:

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

基于時間序列檩电,我們可以將方法調(diào)用排成一條線拄丰。而每個方法調(diào)用則可以看成Java執(zhí)行流中的一個節(jié)點(diǎn)。這個節(jié)點(diǎn)在AOP的術(shù)語中俐末,被稱為Join Point料按,即連接點(diǎn)。 一個Java程序的運(yùn)行的過程卓箫,就是若干個連接點(diǎn)連接起來依次執(zhí)行的過程载矿。

在我們正常的面向?qū)ο蟮乃季S中, 我們考慮的是如何按照時間序列通過方法調(diào)用來實(shí)現(xiàn)我們的業(yè)務(wù)邏輯烹卒。那么闷盔,什么是AOP(即面向切面的編程)呢魂挂?

通常面向?qū)ο蟮某绦颍a都是按照時間序列縱向展開的馁筐,而他們都有一個共性:即都是已方法調(diào)用作為基本執(zhí)行單位展開的涂召。 將方法調(diào)用當(dāng)做一個連接點(diǎn),那么由連接點(diǎn)串起來的程序執(zhí)行流就是整個程序的執(zhí)行過程敏沉。

AOP(Aspect Oriented Programming)則是從另外一個角度來考慮整個程序的果正,AOP將每一個方法調(diào)用,即連接點(diǎn)作為編程的入口盟迟,針對方法調(diào)用進(jìn)行編程秋泳。從執(zhí)行的邏輯上來看,相當(dāng)于在之前縱向的按照時間軸執(zhí)行的程序橫向切入攒菠。相當(dāng)于將之前的程序橫向切割成若干的面迫皱,即Aspect.每個面被稱為切面。

所以辖众,根據(jù)我的理解卓起,AOP本質(zhì)上是針對方法調(diào)用的編程思路。

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

既然AOP是針對切面進(jìn)行的編程的凹炸,那么戏阅,你需要選擇哪些切面(即 連接點(diǎn)Joint Point)作為你的編程對象呢?

因?yàn)榍忻姹举|(zhì)上是每一個方法調(diào)用啤它,選擇切面的過程實(shí)際上就是選擇方法的過程奕筐。那么,被選擇的切面(Aspect)在AOP術(shù)語里被稱為切入點(diǎn)(Point Cut). 切入點(diǎn)實(shí)際上也是從所有的連接點(diǎn)(Join point)挑選自己感興趣的連接點(diǎn)的過程变骡。

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

Spring AOP框架中通過 方法匹配表達(dá)式來表示切入點(diǎn)(Point Cut)离赫,至于詳細(xì)的表達(dá)式語法是什么 不是本文的重點(diǎn),請讀者自行參考Spring相應(yīng)的說明文檔塌碌。

既然AOP是針對方法調(diào)用(連接點(diǎn))的編程渊胸, 現(xiàn)在又選取了你感興趣的自己感興趣的鏈接點(diǎn)---切入點(diǎn)(Point Cut)了,那么誊爹,AOP能對它做什么類型的編程呢蹬刷?AOP能做什么呢瓢捉?

了解這個之前频丘,我們先要知道一個非常重要的問題: 既然AOP是對方法調(diào)用進(jìn)行的編程,那么泡态,AOP如何捕獲方法調(diào)用的呢搂漠? 弄清楚這個問題,你不得不了解設(shè)計(jì)模式中的代理模式了某弦。下面我們先來了解一下引入了代理模式的Java程序執(zhí)行流是什么樣子的桐汤。

3. 引入了代理模式的Java程序執(zhí)行流(AOP實(shí)現(xiàn)的機(jī)制)

我們假設(shè)在我們的Java代碼里而克,都為實(shí)例對象通過代理模式創(chuàng)建了代理對象,訪問這些實(shí)例對象必須要通過代理怔毛,那么员萍,加入了proxy對象的Java程序執(zhí)行流會變得稍微復(fù)雜起來。

我們來看下加入了proxy對象后拣度,Java程序執(zhí)行流的示意圖:

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

由上圖可以看出碎绎,只要想調(diào)用某一個實(shí)例對象的方法時,都會經(jīng)過這個實(shí)例對象相對應(yīng)的代理對象抗果, 即執(zhí)行的控制權(quán)先交給代理對象筋帖。

關(guān)于代理模式

代理模式屬于Java代碼中經(jīng)常用到的、也是比較重要的設(shè)計(jì)模式冤馏。代理模式可以為某些對象除了實(shí)現(xiàn)本身的功能外日麸,提供一些額外的功能,大致作用如下圖所示:

image.gif

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

加入了代理模式的Java程序執(zhí)行流逮光,使得所有的方法調(diào)用都經(jīng)過了代理對象代箭。對于Spring AOP框架而言,它負(fù)責(zé)控制著真?zhèn)€容器內(nèi)部的代理對象涕刚。當(dāng)我們調(diào)用了某一個實(shí)例對象的任何一個非final的public方法時梢卸,整個Spring框架都會知曉。

此時的SpringAOP框架在某種程度上扮演著一個上帝的角色:它知道你在這個框架內(nèi)所做的任何操作副女,你對每一個實(shí)例對象的非final的public方法調(diào)用都可以被框架察覺到蛤高!

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

既然Spring代理層可以察覺到你所做的每一次對實(shí)例對象的方法調(diào)用,那么碑幅,Spring就有機(jī)會在這個代理的過程中插入Spring的自己的業(yè)務(wù)代碼戴陡。

4. Spring AOP的工作原理

前面已經(jīng)介紹了AOP編程首先要選擇它感興趣的連接點(diǎn)----即切入點(diǎn)(Point cut),那么沟涨,AOP能對切入點(diǎn)做什么樣的編程呢恤批? 我們先將代理模式下的某個連接點(diǎn)細(xì)化,你會看到如下這個示意圖所表示的過程:

image.gif

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

為了降低我們對Spring的AOP的理解難度裹赴,我在這里將代理角色的職能進(jìn)行了簡化喜庞,方便大家理解。(注意:真實(shí)的Spring AOP的proxy角色扮演的只能比這復(fù)雜的多棋返,這里只是簡化延都,方便大家理解,請不要先入為主)代理模式的代理角色最起碼要考慮三個階段:

1.在****調(diào)用真正對象的方法之前睛竣,應(yīng)該需要做什么晰房?

  1. 在調(diào)****用真正對象的方法過程中,如果拋出了異常,需要做什么殊者?

3.在調(diào)用真正對象的方法后与境,返回了結(jié)果了,需要做什么猖吴?

AOP對這個方法調(diào)用的編程摔刁,就是針對這三個階段插入自己的業(yè)務(wù)代碼。

現(xiàn)在我們假設(shè)當(dāng)前RealSubject這個角色的類是 org.luanlouis.springlearning.aop.FooService海蔽,當(dāng)前這個連接點(diǎn)對應(yīng)的方法簽名是:public void foo()簸搞。那么上述的代理對象的三個階段將會有以下的處理邏輯:

1. 在調(diào)用真正對象的方法之前,

proxy會告訴Spring AOP: "我將要調(diào)用類org.luanlouis.springlearning.aop.FooService 的public void foo()准潭,在調(diào)用之前趁俊,你有什么處理建議嗎?";

Spring AOP這時根據(jù)proxy提供的類名和方法簽名,然后拿這些信息嘗試匹配是否在其感興趣的切入點(diǎn)內(nèi)刑然,如果在感興趣的切入點(diǎn)內(nèi)寺擂,Spring AOP會返回 MethodBeforeAdvice處理建議,告訴proxy應(yīng)該執(zhí)行的操作泼掠;

2. 在調(diào)用真正對象的方法過程中怔软,如果拋出了異常,需要做什么择镇?

proxy告訴Spring AOP: “我調(diào)用類org.luanlouis.springlearning.aop.FooService 的public void foo()過程中拋出了異常挡逼,你有什么處理建議?”

Spring AOP根據(jù)proxy提供的類型和方法簽名腻豌,確定了在其感興趣的切入點(diǎn)內(nèi)家坎,則返回相應(yīng)的處理建議ThrowsAdvice,告訴proxy這個時期應(yīng)該采取的操作吝梅。

3.在調(diào)用真正對象的方法后虱疏,返回了結(jié)果了,需要做什么苏携?

proxy告訴Spring AOP:"我調(diào)用類org.luanlouis.springlearning.aop.FooService 的public void foo()結(jié)束了做瞪,并返回了結(jié)果你現(xiàn)在有什么處理建議?"右冻;

Spring AOP 根據(jù)proxy提供的類型名和方法簽名装蓬,確定了在其感興趣的切入點(diǎn)內(nèi),則返回AfterReturingAdivce處理建議纱扭,proxy得到這個處理建議牍帚,然后執(zhí)行建議;

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

上述的示意圖中已經(jīng)明確表明了Spring AOP應(yīng)該做什么樣的工作:根據(jù)proxy提供的特定類的特定方法執(zhí)行的特定時期階段給出相應(yīng)的處理建議跪但。要完成該工作履羞,Spring AOP應(yīng)該實(shí)現(xiàn):

1.確定自己對什么類的什么方法感興趣? -----即確定 AOP的切入點(diǎn)(Point Cut)屡久,這個可以通過切入點(diǎn)(Point Cut)表達(dá)式來完成忆首;

2. 對應(yīng)的的類的方法的執(zhí)行特定時期給出什么處理建議?------這個需要Spring AOP提供相應(yīng)的建議 被环,即我們常說的Advice糙及。

image

<figcaption style="margin: 10px 0px 0px; padding: 0px; max-width: 100%; box-sizing: border-box !important; overflow-wrap: break-word !important; font-size: 0.7em; color: rgb(153, 153, 153); text-align: center;">img</figcaption>

到此為止,AOP的基本工作機(jī)制已經(jīng)介紹完畢了筛欢。歡迎各位童鞋關(guān)注交流

原文

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浸锨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子版姑,更是在濱河造成了極大的恐慌柱搜,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件剥险,死亡現(xiàn)場離奇詭異聪蘸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)表制,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進(jìn)店門健爬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人么介,你說我怎么就攤上這事娜遵。” “怎么了壤短?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵设拟,是天一觀的道長。 經(jīng)常有香客問我久脯,道長蒜绽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任桶现,我火速辦了婚禮躲雅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘骡和。我一直安慰自己相赁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布慰于。 她就那樣靜靜地躺著钮科,像睡著了一般。 火紅的嫁衣襯著肌膚如雪婆赠。 梳的紋絲不亂的頭發(fā)上绵脯,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼蛆挫。 笑死赃承,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悴侵。 我是一名探鬼主播瞧剖,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼可免!你這毒婦竟也來了抓于?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤浇借,失蹤者是張志新(化名)和其女友劉穎捉撮,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體妇垢,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡呕缭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了修己。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恢总。...
    茶點(diǎn)故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖睬愤,靈堂內(nèi)的尸體忽然破棺而出片仿,到底是詐尸還是另有隱情,我是刑警寧澤尤辱,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布砂豌,位于F島的核電站,受9級特大地震影響光督,放射性物質(zhì)發(fā)生泄漏阳距。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一结借、第九天 我趴在偏房一處隱蔽的房頂上張望筐摘。 院中可真熱鬧,春花似錦船老、人聲如沸咖熟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽馍管。三九已至,卻和暖如春薪韩,著一層夾襖步出監(jiān)牢的瞬間确沸,已是汗流浹背捌锭。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罗捎,地道東北人观谦。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像宛逗,于是被迫代替她去往敵國和親坎匿。 傳聞我的和親對象是個殘疾皇子盾剩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評論 2 354