一、AOP 概述
AOP 是 Aspect Oriented Programing 的簡稱,面向切面的編碼;
AOP 的工作重心是如何將增強應用到目標對象的連接點上:
第一,通過切點和增強定位到連接點上;
第二,在增強中編寫切面的代碼;
1.1、連接點 - Joinpoint
Spring 僅支持方法的連接點;
連接點由兩個個信息確定:
(1)一是用方法表示的程序 執行點;
(2)二是用相對位置表示的 方位;
如在 Test.foo() 方法執行前的連接點,執行點是 Test.foo(),方位是該方法執行前的位置;
1.2、切點 - Pointcut
切點可以用來定位連接點;
在 Spring 中,切點通過 org.springframework.aop.Pointcut 接口進行描述,使用類和方法作爲連接點的查詢條件,Spring AOP 的規則解析引擎負責解析切點所設定的查詢條件,找到連接點,準備的說是 執行點;
Spring 通過 org.springframework.aop.Pointcut 接口描述切點:
- ClassFilter:定位到某些特定類上;
- MethodMatcher:定位到某些特定的方法上;
1.3、增強 - Advice
增強是織入目標類連接點上的 一段程序代碼;
在 Spring 中,增強還擁有一個和連接點有關的信息,就是 執行點的方位,
如 BeforeAdvice、AfterReturningAdvice、ThrowsAdvice 等;
1.4、目標對象 - Target
增強邏輯的織入目標類;
1.5、引介 - Introduction
引介是一種特殊的增強,它可以爲類添加一些屬性和方法,比如間接的幫業務類實現某個接口;
1.6、織入 - Weaving
織入就是將增強或者引介添加到目標類的具體連接點上的過程;
(1)編譯期織入,要求使用特殊的 Java 編譯器;
(2)類裝載期織入,要求使用特殊的類裝載器;
(3)動態代理織入,在運行期爲目標類添加增強生成子類的方式;
Spring 採用的是 動態代理織入,而 AspectJ 採用 編譯期織入和類裝載期織入;
1.7、代理 - Proxy
一個類被 AOP 織入增強後,會產生一個結果類,它是 融合了原類和增強邏輯的代理類;
1.8、切面 - Aspect
切面由 切點 和 增強(引介)組成;
包含了 橫切代碼 和 連接點 信息;
二、基礎知識 - 動態代理技術
Spring AOP 使用動態代理技術在運行期織入增強的代碼,它使用了兩種代理機制:
(1)一種是基於 JDK 的動態代理;
(2)一種是基於 CGLib 的動態代理;
之所以需要兩種動態代理機制,是因爲 JDK 本身只提供接口的代理,而不支持類的代理;
二者的區別可以參考我之前的文章:Spring AOP 兩種動態代理機制
JDK 的動態代理主要涉及 java.lang.reflect 包中的兩個類:
(1)Proxy ;
(2)InvocationHandler(是一個接口);
原始的 AOP 有三個問題:
(1)目標類的所有方法都添加了性能監視橫切邏輯,但是我們只希望對業務類中的某些特定的方法添加橫切邏輯;
(2)通過硬編碼的方式指定了織入橫切邏輯的織入點,及在方法開始前還是結束前織入代碼;
(3)手工編寫代理實例的創建過程,爲不同的類創建代理時,需要分別編寫相應的創建代碼,無法做到通用;
Spring AOP 則是通過 Pointcut 指定哪些類的哪些方法需要織入橫切邏輯,通過 Advice 描述橫切邏輯和方法的具體織入方位;其通過 切面 Advisor 將 Pointcut 和 Advice 組裝起來,這樣就可以利用 JDK 和 CGLib 採用統一的方式爲目標對象創建織入切面的代理對象了;
三、問題
3.1、spring AOP無法攔截內部方法調用(方法 A 內部調用了本類的方法 B,B沒有得到增強)?
方法內部之間調用的時候,不會使用被增強的代理類,而是直接調用未被增強的原類方法;
解決方法有很多,將增強類寫入原類中屬性中,然後使用該屬性調用 B 方法,或者使用如下方法:
(1)配置文件添加 expose-proxy 屬性,值爲 true;
<aop:aspectj-autoproxy proxy-target-class="true" expose-proxy="true">
(2)內部調用的地方使用,使用前先判斷 AopContext.currentProxy() 是否存在;
((類名) AopContext.currentProxy()).方法名