Spring AOP 歷險(xiǎn)記(一)


AOP 簡介

在學(xué)習(xí) Spring AOP 之前,我們先來了解一下 AOP翎迁。我們都聽過面向?qū)ο缶幊?OOP)祈坠,那么 AOP 到底是什么呢?中文意思我想大家應(yīng)該都是非常的耳熟能詳了瘩例,中文翻譯過來就是面向切面編程啊胶,當(dāng)然也有些人會(huì)翻譯成面向方面編程,不過還是感覺面向切面編程更好聽點(diǎn)垛贤。來看下官方的定義:

AOP is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns.

翻譯過來就是焰坪,AOP 是一種編程范式,旨在通過分離橫切關(guān)注點(diǎn)來增加模塊化聘惦。它通過向現(xiàn)有代碼添加其他行為而不修改現(xiàn)有代碼本身來實(shí)現(xiàn)某饰。相反,我們可以分別聲明這個(gè)新代碼和這些新行為。

【櫻木天亥注】面向?qū)ο缶幊痰幕締卧?code>類(class)黔漂,而面向切面編程的基本單元是切面(aspect)诫尽。

Spring AOP 是什么

看完了上面 AOP 的定義,那么接下來我們來看下什么是 Spring AOP 呢炬守?

Spring AOP 在 Spring 應(yīng)用程序?qū)崿F(xiàn)了面向切面編程牧嫉。在 AOP 中,這些切面實(shí)現(xiàn)了一些關(guān)注點(diǎn)的模塊化劳较,諸如事務(wù)管理驹止,日志記錄或跨越多種類型和對(duì)象的安全性(通常稱為橫切關(guān)注點(diǎn))浩聋。

Spring AOP 的主要術(shù)語

大多數(shù)的技術(shù)都會(huì)形成自己的一套術(shù)語观蜗,Spring AOP 也不例外。而 Spring AOP 最主要的三個(gè)術(shù)語分別是通知(Advice)衣洁、切點(diǎn)(Pointcut)連接點(diǎn)(Join point)墓捻。如圖,展示了這三者是如何聯(lián)系到一塊的坊夫。

image

  • 建言(Advice) ?? 通知定義了切面是什么以及何時(shí)使用砖第,是在方法執(zhí)行之前或之后要采取的實(shí)際操作。Spring 的面向切面編程框架在程序執(zhí)行期間調(diào)用的時(shí)間代碼段。

  • 切點(diǎn)(Pointcut) ?? 切點(diǎn)是一個(gè)與連接點(diǎn)相匹配的表達(dá)式,用于確定是否需要執(zhí)行建言(Advice)侈离。切點(diǎn)有助于確定切面所通知的連接點(diǎn)的范圍唤衫,它的定義會(huì)匹配通知所要織入的一個(gè)或多個(gè)連接點(diǎn)。切點(diǎn)使用與連接點(diǎn)匹配的不同類型的表達(dá)式滤馍,在 Spring 框架中使用 Aspect 切點(diǎn)表達(dá)式語言(SpEL)。

  • 連接點(diǎn)(Join Point) ?? 連接點(diǎn)在應(yīng)用程序中是具體的點(diǎn),例如方法執(zhí)行考赛,異常處理,改變對(duì)象的變量值等莉测。在 Spring AOP 中一個(gè)連接點(diǎn)總是一個(gè)方法的執(zhí)行點(diǎn)颜骤,即只有方法執(zhí)行點(diǎn)。

Spring AOP 其他常用的術(shù)語

  • 切面(Aspect) ?? 一個(gè)切面是跨越多個(gè)類的橫切關(guān)注點(diǎn)的模塊化捣卤,比如事務(wù)管理忍抽。切面可以通過 XML 的方式來配置,也可以通過 Spring AspectJ 集成使用 @Aspect 注解來注入切面董朝。切面是通知(Advice)和切點(diǎn)(Pointcut)的結(jié)合梯找。通知和切點(diǎn)共同定義了切面餓的全部內(nèi)容——它是什么,在何時(shí)和何處完成了其功能益涧。
  • Introductions ?? introduction 允許向現(xiàn)有的類添加新的方法或?qū)傩孕獯浮?梢詫⑿碌姆椒ê蛯?shí)例變量引入現(xiàn)有類而無需改變它們,為它們提供新的狀態(tài)和行為久免。Spring AOP 允許我們?yōu)?code>目標(biāo)對(duì)象引入新的接口和對(duì)應(yīng)的實(shí)現(xiàn)浅辙。
  • Target Object ?? 織入 advice 的目標(biāo)對(duì)象,目標(biāo)對(duì)象也被稱為 Target object阎姥。Spring AOP 是使用運(yùn)行時(shí)代理實(shí)現(xiàn)切面的记舆,因此該對(duì)象始終是代理對(duì)象,指的不是原來的類呼巴,而是織入 advice 后所產(chǎn)生的代理類泽腮。這意味著在運(yùn)行時(shí)創(chuàng)建子類,其中衣赶,覆蓋目標(biāo)方法并根據(jù)配置包含建言(advice)诊赊。
  • Weaving ?? 將 aspect 和其他對(duì)象連接起來, 并創(chuàng)建代理對(duì)象(adviced object)的過程。這可以在編譯時(shí)府瞄,加載時(shí)或在運(yùn)行時(shí)完成碧磅,而 Spring AOP 是在運(yùn)行時(shí)執(zhí)行編織。

織入(weaving)可以在目標(biāo)對(duì)象的幾個(gè)不同的生命周期執(zhí)行:

  • 編譯期織入(Compile Time):編譯目標(biāo)類時(shí)會(huì)織入切面遵馆,同時(shí)鲸郊,要求要有一個(gè)特殊的編譯器。
  • 類裝載期織入(Classload Time):當(dāng)目標(biāo)類加載到 JVM 中時(shí)會(huì)織入切面货邓,同時(shí)秆撮,要求要有一個(gè)特殊的類裝載器。
  • 運(yùn)行期織入(Runtime):在應(yīng)用程序執(zhí)行期間的某些時(shí)間點(diǎn)織入切面换况。AOP 容器將動(dòng)態(tài)生成代理對(duì)象职辨,該代理對(duì)象將在織入切面時(shí)委托給目標(biāo)對(duì)象。

【櫻木天亥注】AspectJ 織入編譯器屬于編譯期織入复隆;AspectJ 5 的裝載期織入(LTW拨匆,load-time weaving)屬于類裝載期織入;Spring AOP 織入切面則屬于運(yùn)行期織入挽拂。

Spring AOP 的優(yōu)點(diǎn)

1惭每、AOP 是非侵入性的

  • 允許開發(fā)人員更加專注于業(yè)務(wù)邏輯的開發(fā),而不是受困于橫切關(guān)注點(diǎn)的問題
  • 類可以通過切面獲得建言而無需將 Spring AOP 相關(guān)類或接口添加到類中

2亏栈、AOP 是通過純 Java 語言實(shí)現(xiàn)的

  • 這意味著我們不需要特殊的編譯單元或特殊的類加載器

3台腥、通過 Spring IOC 容器實(shí)現(xiàn)依賴注入

4、能夠?qū)⒍鄠€(gè)橫切關(guān)注點(diǎn)織入類中而無需調(diào)用這些類的橫切關(guān)注點(diǎn)

5绒北、能夠集中或模塊化橫切關(guān)注點(diǎn)黎侈,使得維持和改變這些切點(diǎn)變得容易維護(hù)

6、提供了 XML 或 @AspectJ 注解的多種靈活的方式來創(chuàng)建切面

7闷游、易于配置

Spring AOP 的缺點(diǎn)

1峻汉、使用基于代理的 AOP贴汪,所以僅支持方法級(jí)別的建言,不支持屬性級(jí)別的建言

2休吠、當(dāng)且僅當(dāng)方法的可見性為 public 時(shí)扳埂,才支持建言(Advice)

  • 方法可見性為 private,protected 或者 default 時(shí)不支持建言

3瘤礁、一個(gè)切面不能作為另一個(gè)切面的 advice target 阳懂。

  • 如果一個(gè)類被 @Aspect 標(biāo)注, 則這個(gè)類就不能是其他切面的目標(biāo)對(duì)象了, 因?yàn)槭褂?@Aspect 后, 這個(gè)類就會(huì)被排除在 auto-proxying 機(jī)制之外。

4柜思、由于性能問題岩调,建言不適用于細(xì)粒度的對(duì)象,只適合粗粒度的對(duì)象赡盘。

建言(advice)的類型

建言(Advice)的類型有一下五種号枕。

  • Before advice 在連接點(diǎn)之前執(zhí)行,但是不能阻止執(zhí)行流程進(jìn)入連接點(diǎn)亡脑,即不能人為地在 before advice 代碼中阻止 join point 中代碼的執(zhí)行堕澄。
  • After returning advice 在連接點(diǎn)正常執(zhí)行完成后執(zhí)行邀跃,例如霉咨,一個(gè)方法執(zhí)行完成不拋異常則執(zhí)行這個(gè) advice
  • After throwing advice 當(dāng)一個(gè)連接點(diǎn)(join point)拋出異常后退出,則執(zhí)行該 advice
  • After advice 無論連接點(diǎn)是正常退出還是拋出了異常退出拍屑,都會(huì)執(zhí)行這個(gè) advice
  • Around advice 圍繞連接點(diǎn)的建言途戒,比如方法調(diào)用。這是最常用的 advice僵驰,在 join point 執(zhí)行前和 join point 執(zhí)行后都執(zhí)行的 advice喷斋。它還負(fù)責(zé)選擇是否繼續(xù)加入連接點(diǎn)還是返回自身的返回值,或者拋出異常來加速方法的執(zhí)行蒜茴。

AOP 代理(proxy)

我們都知道星爪,Spring AOP 底層主要有兩種實(shí)現(xiàn),一種是 JDK 動(dòng)態(tài)代理粉私,一種是 CGLIB 動(dòng)態(tài)代理顽腾。而 Spring AOP 代理的實(shí)現(xiàn)是通過 JDK 動(dòng)態(tài)代理來創(chuàng)建一個(gè)具有目標(biāo)類和通知調(diào)用的 Proxy 類,這些類被稱為 AOP 代理類诺核。

那么抄肖,JDK 動(dòng)態(tài)代理和 CGLIB 動(dòng)態(tài)代理最大不同是什么呢?前者的原理是 JDK 反射窖杀,并且只支持 Java 接口的代理漓摩;后者的原理是繼承(extend)與覆寫(override),因此能支持普通的 Java 類的代理入客。兩種方式都是動(dòng)態(tài)代理管毙,即運(yùn)行時(shí)實(shí)時(shí)生成代理腿椎。但是,由于JVM 的限制夭咬,CGLIB 無法替換被代理類已經(jīng)被載入的字節(jié)碼酥诽,只能生成并載入一個(gè)新的子類作為代理類,被代理類的字節(jié)碼依然存在于JVM 中皱埠。

Spring AOP 示例

兩種聲明方式:

1肮帐、XML 方式

<!-- 使用 XML 方式,啟用 AspectJ 風(fēng)格的 Spring AOP -->
<aop:aspectj-autoproxy />

2边器、Java 配置的方式

//使用 Java 配置的方式训枢,啟用 AspectJ 風(fēng)格的 Spring AOP
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}

定義切面(Aspect)

@Component
@Aspect
public class EmployeeAspect {
    @Before("execution(* EmployeeManager.getEmployeeById(..))") //point-cut expression
    public void logBefore(JoinPoint joinPoint) {
        ……
    }
}

【櫻木天亥注】僅僅使用 @Aspect 注解, 并不能將一個(gè) Java 對(duì)象轉(zhuǎn)換為 Bean, 因此我們還需要使用類似 @Component 之類的注解。

切點(diǎn)表達(dá)式

@Before("execution(* EmployeeManager.getEmployeeById(..))") //point-cut expression
    public void logBeforeAdvice(JoinPoint joinPoint) {
        ……
    }

【櫻木天亥注】
1忘巧、execution( EmployeeManager.getEmployeeById(..)) 這個(gè)切點(diǎn)表達(dá)式表示這個(gè)切點(diǎn)將會(huì)匹配 EmployeeManager 類下面所有的 getEmployeeById 方法恒界,而不管它的參數(shù)類型和數(shù)量是什么。
2砚嘴、@Before 表示當(dāng)前的 Advice 是在連接點(diǎn)(Join point)之前執(zhí)行十酣。

連接點(diǎn)(Join point)

//@Component
public class EmployeeManager
{
    public EmployeeDTO getEmployeeById(Integer employeeId) {
        System.out.println("Method getEmployeeById() called");
        return new EmployeeDTO();
    }
}

【櫻木天亥注】如果前面定義切面(Aspect)時(shí)沒有添加 @Component 注解,則這里應(yīng)該要加上际长,不然可能不能將一個(gè) Java 對(duì)象轉(zhuǎn)換為 Bean耸采。

聲明建言(Advice)

Advice 與切點(diǎn)表達(dá)式緊密關(guān)聯(lián),會(huì)在匹配的連接點(diǎn)執(zhí)行前工育,執(zhí)行后或者周圍(around)執(zhí)行虾宇。切點(diǎn)表達(dá)式既可以是簡單的一個(gè) pointcut 名字的引用, 又可以是完整的 pointcut 表達(dá)式。這里簡單的舉幾個(gè)例子說明如下如绸。

Around advice

@Around("execution(* EmployeeManager.getEmployeeById(..))")
    public Object logAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){
        ……
    }

Before advice

@Before("execution(* EmployeeManager.getEmployeeById(..))") //point-cut expression
    public void logBeforeAdvice(JoinPoint joinPoint) {
        ……
    }



參考來源
1嘱朽、Introduction to Spring AOP
2、Spring AOP Tutorial Example
3怔接、Spring AOP Example Tutorial – Aspect, Advice, Pointcut, JoinPoint, Annotations, XML Configuration
4搪泳、Overview of Spring Aspect Oriented Programming (AOP)
5、Spring AOP 是什么扼脐?
6岸军、徹底征服 Spring AOP 之 理論篇
7、《Spring實(shí)戰(zhàn)(第 4 版)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末谎势,一起剝皮案震驚了整個(gè)濱河市凛膏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌脏榆,老刑警劉巖猖毫,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異须喂,居然都是意外死亡吁断,警方通過查閱死者的電腦和手機(jī)趁蕊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仔役,“玉大人掷伙,你說我怎么就攤上這事∮直” “怎么了任柜?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長沛厨。 經(jīng)常有香客問我宙地,道長,這世上最難降的妖魔是什么逆皮? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任宅粥,我火速辦了婚禮,結(jié)果婚禮上电谣,老公的妹妹穿的比我還像新娘秽梅。我一直安慰自己,他們只是感情好剿牺,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布企垦。 她就那樣靜靜地躺著,像睡著了一般牢贸。 火紅的嫁衣襯著肌膚如雪竹观。 梳的紋絲不亂的頭發(fā)上镐捧,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天潜索,我揣著相機(jī)與錄音,去河邊找鬼懂酱。 笑死竹习,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的列牺。 我是一名探鬼主播整陌,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瞎领!你這毒婦竟也來了泌辫?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤九默,失蹤者是張志新(化名)和其女友劉穎震放,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驼修,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡殿遂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年诈铛,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片墨礁。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡幢竹,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恩静,到底是詐尸還是另有隱情焕毫,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布驶乾,位于F島的核電站咬荷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏轻掩。R本人自食惡果不足惜幸乒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望唇牧。 院中可真熱鬧罕扎,春花似錦、人聲如沸丐重。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽扮惦。三九已至臀蛛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間崖蜜,已是汗流浹背浊仆。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留豫领,地道東北人抡柿。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像等恐,于是被迫代替她去往敵國和親洲劣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359