基本知識(shí)
其實(shí), 接觸了這么久的 AOP, 我感覺(jué), AOP 給人難以理解的一個(gè)關(guān)鍵點(diǎn)是它的概念比較多, 而且坑爹的是, 這些概念經(jīng)過(guò)了中文翻譯后, 變得面目全非, 相同的一個(gè)術(shù)語(yǔ), 在不同的翻譯下, 含義總有著各種莫名其妙的差別. 鑒于此, 我在本章的開(kāi)頭, 著重為為大家介紹一個(gè) Spring AOP 的各項(xiàng)術(shù)語(yǔ)的基本含義. 為了術(shù)語(yǔ)傳達(dá)的準(zhǔn)確性, 我在接下來(lái)的敘述中, 能使用英文術(shù)語(yǔ)的地方, 盡量使用英文.
什么是 AOP
AOP(Aspect-Oriented Programming), 即 面向切面編程, 它與 OOP( Object-Oriented Programming, 面向?qū)ο缶幊? 相輔相成, 提供了與 OOP 不同的抽象軟件結(jié)構(gòu)的視角.
在 OOP 中, 我們以類(class)作為我們的基本單元, 而 AOP 中的基本單元是 Aspect(切面)
術(shù)語(yǔ)
Aspect(切面)
aspect 由 pointcount 和 advice 組成, 它既包含了橫切邏輯的定義, 也包括了連接點(diǎn)的定義. Spring AOP就是負(fù)責(zé)實(shí)施切面的框架, 它將切面所定義的橫切邏輯織入到切面所指定的連接點(diǎn)中.
AOP的工作重心在于如何將增強(qiáng)織入目標(biāo)對(duì)象的連接點(diǎn)上, 這里包含兩個(gè)工作:
- 如何通過(guò) pointcut 和 advice 定位到特定的 joinpoint 上
- 如何在 advice 中編寫(xiě)切面代碼.
可以簡(jiǎn)單地認(rèn)為, 使用 @Aspect 注解的類就是切面.
advice(增強(qiáng))
由 aspect 添加到特定的 join point(即滿足 point cut 規(guī)則的 join point) 的一段代碼.
許多 AOP框架, 包括 Spring AOP, 會(huì)將 advice 模擬為一個(gè)攔截器(interceptor), 并且在 join point 上維護(hù)多個(gè) advice, 進(jìn)行層層攔截.
例如 HTTP 鑒權(quán)的實(shí)現(xiàn), 我們可以為每個(gè)使用 RequestMapping 標(biāo)注的方法織入 advice, 當(dāng) HTTP 請(qǐng)求到來(lái)時(shí), 首先進(jìn)入到 advice 代碼中, 在這里我們可以分析這個(gè) HTTP 請(qǐng)求是否有相應(yīng)的權(quán)限, 如果有, 則執(zhí)行 Controller, 如果沒(méi)有, 則拋出異常. 這里的 advice 就扮演著鑒權(quán)攔截器的角色了.
連接點(diǎn)(join point)
a point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.
程序運(yùn)行中的一些時(shí)間點(diǎn), 例如一個(gè)方法的執(zhí)行, 或者是一個(gè)異常的處理.
在 Spring AOP 中, join point 總是方法的執(zhí)行點(diǎn), 即只有方法連接點(diǎn).
切點(diǎn)(point cut)
匹配 join point 的謂詞(a predicate that matches join points).
Advice 是和特定的 point cut 關(guān)聯(lián)的, 并且在 point cut 相匹配的 join point 中執(zhí)行.
在 Spring 中, 所有的方法都可以認(rèn)為是 joinpoint, 但是我們并不希望在所有的方法上都添加 Advice, 而 pointcut 的作用就是提供一組規(guī)則(使用 AspectJ pointcut expression language 來(lái)描述) 來(lái)匹配joinpoint, 給滿足規(guī)則的 joinpoint 添加 Advice.
關(guān)于join point 和 point cut 的區(qū)別
在 Spring AOP 中, 所有的方法執(zhí)行都是 join point. 而 point cut 是一個(gè)描述信息, 它修飾的是 join point, 通過(guò) point cut, 我們就可以確定哪些 join point 可以被織入 Advice. 因此 join point 和 point cut 本質(zhì)上就是兩個(gè)不同緯度上的東西.
advice 是在 join point 上執(zhí)行的, 而 point cut 規(guī)定了哪些 join point 可以執(zhí)行哪些 advice
introduction
為一個(gè)類型添加額外的方法或字段. Spring AOP 允許我們?yōu)?目標(biāo)對(duì)象 引入新的接口(和對(duì)應(yīng)的實(shí)現(xiàn)). 例如我們可以使用 introduction 來(lái)為一個(gè) bean 實(shí)現(xiàn) IsModified 接口, 并以此來(lái)簡(jiǎn)化 caching 的實(shí)現(xiàn).
目標(biāo)對(duì)象(Target)
織入 advice 的目標(biāo)對(duì)象. 目標(biāo)對(duì)象也被稱為 advised object.
因?yàn)?Spring AOP 使用運(yùn)行時(shí)代理的方式來(lái)實(shí)現(xiàn) aspect, 因此 adviced object 總是一個(gè)代理對(duì)象(proxied object)
注意, adviced object 指的不是原來(lái)的類, 而是織入 advice 后所產(chǎn)生的代理類.
AOP proxy
一個(gè)類被 AOP 織入 advice, 就會(huì)產(chǎn)生一個(gè)結(jié)果類, 它是融合了原類和增強(qiáng)邏輯的代理類.
在 Spring AOP 中, 一個(gè) AOP 代理是一個(gè) JDK 動(dòng)態(tài)代理對(duì)象或 CGLIB 代理對(duì)象.
織入(Weaving)
將 aspect 和其他對(duì)象連接起來(lái), 并創(chuàng)建 adviced object 的過(guò)程.
根據(jù)不同的實(shí)現(xiàn)技術(shù), AOP織入有三種方式:
- 編譯器織入, 這要求有特殊的Java編譯器.
- 類裝載期織入, 這需要有特殊的類裝載器.
- 動(dòng)態(tài)代理織入, 在運(yùn)行期為目標(biāo)類添加增強(qiáng)(Advice)生成子類的方式.
Spring 采用動(dòng)態(tài)代理織入, 而AspectJ采用編譯器織入和類裝載期織入.
advice 的類型
before advice, 在 join point 前被執(zhí)行的 advice. 雖然 before advice 是在 join point 前被執(zhí)行, 但是它并不能夠阻止 join point 的執(zhí)行, 除非發(fā)生了異常(即我們?cè)?before advice 代碼中, 不能人為地決定是否繼續(xù)執(zhí)行 join point 中的代碼)
after return advice, 在一個(gè) join point 正常返回后執(zhí)行的 advice
after throwing advice, 當(dāng)一個(gè) join point 拋出異常后執(zhí)行的 advice
after(final) advice, 無(wú)論一個(gè) join point 是正常退出還是發(fā)生了異常, 都會(huì)被執(zhí)行的 advice.
around advice, 在 join point 前和 joint point 退出后都執(zhí)行的 advice. 這個(gè)是最常用的 advice.
關(guān)于 AOP Proxy
Spring AOP 默認(rèn)使用標(biāo)準(zhǔn)的 JDK 動(dòng)態(tài)代理(dynamic proxy)技術(shù)來(lái)實(shí)現(xiàn) AOP 代理, 通過(guò)它, 我們可以為任意的接口實(shí)現(xiàn)代理.
如果需要為一個(gè)類實(shí)現(xiàn)代理, 那么可以使用 CGLIB 代理. 當(dāng)一個(gè)業(yè)務(wù)邏輯對(duì)象沒(méi)有實(shí)現(xiàn)接口時(shí), 那么Spring AOP 就默認(rèn)使用 CGLIB 來(lái)作為 AOP 代理了. 即如果我們需要為一個(gè)方法織入 advice, 但是這個(gè)方法不是一個(gè)接口所提供的方法, 則此時(shí) Spring AOP 會(huì)使用 CGLIB 來(lái)實(shí)現(xiàn)動(dòng)態(tài)代理. 鑒于此, Spring AOP 建議基于接口編程, 對(duì)接口進(jìn)行 AOP 而不是類.
本文由 yongshun 發(fā)表于個(gè)人博客, 采用 署名-相同方式共享 3.0 中國(guó)大陸許可協(xié)議.
Email: yongshun1228@gmail .com
本文標(biāo)題為: 徹底征服 Spring AOP 之 理論篇
本文鏈接為: https://segmentfault.com/a/1190000007469968