Spring AOP:原理、 通知、連線點、切點、切面、表示式
0:Spring AOP 原理
簡單說說 AOP 的設計:
-
每個 Bean 都會被 JDK 或者 Cglib 代理。取決於是否有介面。
-
每個 Bean 會有多個“方法攔截器”。注意:攔截器分為兩層,外層由 Spring 核心控制流程,內層攔截器是使用者設定,也就是 AOP。
-
當代理方法被呼叫時,先經過外層攔截器,外層攔截器根據方法的各種資訊判斷該方法應該執行哪些“內層攔截器”。內層攔截器的設計就是職責連的設計。
可以將 AOP 分成 2 個部分:
第一:代理的建立;
第二:代理的呼叫。
開始分析(扯):
-
代理的建立(按步驟):
-
首先,需要建立代理工廠,代理工廠需要 3 個重要的資訊:攔截器陣列,目標物件介面陣列,目標物件。
-
建立代理工廠時,預設會在攔截器陣列尾部再增加一個預設攔截器 —— 用於最終的呼叫目標方法。
-
當呼叫 getProxy 方法的時候,會根據介面數量大餘 0 條件返回一個代理物件(JDK or Cglib)。
-
注意:建立代理物件時,同時會建立一個外層攔截器,這個攔截器就是 Spring 核心的攔截器。用於控制整個 AOP 的流程。
-
代理的呼叫
-
當對代理物件進行呼叫時,就會觸發外層攔截器。
-
外層攔截器根據代理配置資訊,建立內層攔截器鏈。建立的過程中,會根據表示式判斷當前攔截是否匹配這個攔截器。而這個攔截器鏈設計模式就是職責鏈模式。
-
當整個鏈條執行到最後時,就會觸發建立代理時那個尾部的預設攔截器,從而呼叫目標方法。最後返回。
Spring對AOP的支援
Spring中AOP代理由Spring的IOC容器負責生成、管理,其依賴關係也由IOC容器負責管理。因此,AOP代理可以直接使用容器中的其它bean例項作為目標,這種關係可由IOC容器的依賴注入提供。Spring建立代理的規則為:
1、預設使用Java動態代理來建立AOP代理,這樣就可以為任何介面例項建立代理了
2、當需要代理的類不是代理介面的時候,Spring會切換為使用CGLIB代理,也可強制使用CGLIB
AOP程式設計其實是很簡單的事情,縱觀AOP程式設計,程式設計師只需要參與三個部分:
1、定義普通業務元件
2、定義切入點,一個切入點可能橫切多個業務元件
3、定義增強處理,增強處理就是在AOP框架為普通業務元件織入的處理動作
所以進行AOP程式設計的關鍵就是定義切入點和定義增強處理,一旦定義了合適的切入點和增強處理,AOP框架將自動生成AOP代理,即:代理物件的方法=增強處理+被代理物件的方法。
1:知識背景
軟體系統可以看成是由一組關注點組成的,其中,直接的業務關注點,是直切關注點。而為直切關注點提供服務的,就是橫切關注點。
2:面向切面的基本原理
什麼是面向切面程式設計
橫切關注點:影響應用多處的功能(安全、事務、日誌)
切面:
橫切關注點被模組化為特殊的類,這些類稱為切面
優點:
每個關注點現在都集中於一處,而不是分散到多處程式碼中
服務模組更簡潔,服務模組只需關注核心程式碼。
AOP 術語
- 通知:
- 定義:切面也需要完成工作。在 AOP 術語中,切面的工作被稱為通知。
- 工作內容:通知定義了切面是什麼以及何時使用。除了描述切面要完成的工作,通知還解決何時執行這個工作。
- Spring 切面可應用的 5 種通知型別:
- Before——在方法呼叫之前呼叫通知
- After——在方法完成之後呼叫通知,無論方法執行成功與否
- After-returning——在方法執行成功之後呼叫通知
- After-throwing——在方法丟擲異常後進行通知
- Around——通知包裹了被通知的方法,在被通知的方法呼叫之前和呼叫之後執行自定義的行為
- 連線點:
- 定義:連線點是一個應用執行過程中能夠插入一個切面的點。
- 連線點可以是呼叫方法時、丟擲異常時、甚至修改欄位時、
- 切面程式碼可以利用這些點插入到應用的正規流程中
- 程式執行過程中能夠應用通知的所有點。
- 切點:
- 定義:如果通知定義了“什麼”和“何時”。那麼切點就定義了“何處”。切點會匹配通知所要織入的一個或者多個連線點。
- 通常使用明確的類或者方法來指定這些切點。
- 作用:定義通知被應用的位置(在哪些連線點)
- 切面:
- 定義:切面是通知和切點的集合,通知和切點共同定義了切面的全部功能——它是什麼,在何時何處完成其功能。
- 引入:
- 引入允許我們向現有的類中新增方法或屬性
- 織入:
- 織入是將切面應用到目標物件來建立的代理物件過程。
- 切面在指定的連線點被織入到目標物件中,在目標物件的生命週期中有多個點可以織入
- 編譯期——切面在目標類編譯時期被織入,這種方式需要特殊編譯器。AspectJ的織入編譯器就是以這種方式織入切面。
- 類載入期——切面在類載入到
- JVM ,這種方式需要特殊的類載入器,他可以在目標類被引入應用之前增強該目標類的位元組碼。AspectJ5 的 LTW 就支援這種織入方式
- 執行期——切面在應用執行期間的某個時刻被織入。一般情況下,在織入切面時候,AOP 容器會為目標物件動態的建立代理物件。Spring AOP 就是以這種方式織入切面。
3:Spring 對 AOP 的支援
- 並不是所有的 AOP 框架都是一樣的,他們在連線點模型上可能有強弱之分。
- 有些允許對欄位修飾符級別應用通知
- 有些支援方法呼叫連線點
- Spring 提供的 4 種各具特色的 AOP 支援
- 基於代理的經典 AOP;
- @AspectJ 註解驅動的切面;
- 純 POJO 切面;
- 注入式 AspectJ 切面; Spring
- 在執行期間通知物件
- 通過在代理類中織入包裹切面,Spring 在執行期間將切面織入到 Spring 管理的 Bean 中。
代理類封裝了目標類,並攔截被通知的方法呼叫,再將呼叫轉發給真正的目標 Bean
當攔截到方法呼叫時,在呼叫目標 Bean 方法之前,代理會執行切面邏輯。
當真正應用需要被代理的 Bean 時,Spring 才建立代理物件。如果使用 ApplicationContext,在 ApplicationContext 從 BeanFactory 中載入所有 Bean 時,Spring 建立代理物件,因為 Spring 在執行時候建立代理物件,所以我們不需要特殊的編譯器來織入 Spring AOP 的切面。
- 通過在代理類中織入包裹切面,Spring 在執行期間將切面織入到 Spring 管理的 Bean 中。
- Spring 支援方法建立連線點
- 因為 Spring 基於動態代理,所以 Spring 只支援方法連線點。
- Spring 缺失對欄位連線點的支援,無法讓我們更加細粒度的通知,例如攔截物件欄位的修改
- Spring 缺失對構造器連線點支援,我發在 Bean 建立時候進行通知。
4:使用切點選擇連線點
- 切點用於準確定位,確定在什麼地方應用切面通知。
- Spring 定義切點
- 在 Spring AOP 中,需要使用 AspectJ 的切點表示式來定義切點。
AspectJ 指示器 | 描述 |
---|---|
arg () | 限制連線點的指定引數為指定型別的執行方法 |
@args () | 限制連線點匹配引數由指定註解標註的執行方法 |
execution () | 用於匹配連線點的執行方法 |
this () | 限制連線點匹配 AOP 代理的 Bean 引用為指定型別的類 |
target () | 限制連線點匹配特定的執行物件,這些物件對應的類要具備指定型別註解 |
within() | 限制連線點匹配指定型別 |
@within() | 限制連線點匹配指定註釋所標註的型別(當使用 Spring AOP 時,方法定義在由指定的註解所標註的類裡) |
@annotation | 限制匹配帶有指定註釋的連線點 |
1. 建立自己的切點
- execution( ) 指示器選擇 Instrument 的 play( ) 方法。
方法表示式是以 * 號開頭,標識了我們不關心的方法返回值的型別。
* 後我們指定了許可權定類名和方法名。
對於方法的引數列表,使用(..)標識切點選擇任意的 play( ) 方法,無論入參是什麼。
- 假設我們需要匹配切點僅匹配 com.Springinaction.springidol 包。可以使用 within()
注意 && 是將 execution( ) 和 within( ) 連線起來,形成的 and 關係。同理也可以使用 || 或關係、!非關係
- 建立 Spring 的 bean( ) 指示器
Spring 2.5 引入一個新的 bean( ) 指示器,該指示器允許我們在切點表示式中使用 Bean ID 來標識 Bean
bean( ) 使用 Bean ID 或 Bean 名稱作為引數來限制切點只匹配特定 Bean。
如下,我們希望執行 Instrument 的 play( ) 方法時候應用通知,但限定 Bean 的 ID 為 eddie
還可以使用非操作作為除了指定 ID 的 Bean 以外的其他 Bean應用通知
在此場景下,切面會通知被編織到所有 ID 不為 eddie 的 Bean 中
5:在 XML 中宣告切面
Spring 的 AOP 配置元素簡化了基於 POJO 切面宣告
AOP 配置元素 |
描述 |
---|---|
aop : advisor | 定義 AOP 通知器 |
aop : after | 定義 AOP 後置通知(不管被通知方法是否執行成功) |
aop : after-returing | 定義 AOP after-returing 通知 |
aop : after-throwing | 定義 AOP after-throwing 通知 |
aop : around | 定義 AOP 環繞通知 |
aop : aspect | 定義切面 |
aop : aspectj-autoproxy | 啟動 @AspectJ 註解驅動的切面 |
aop : before | 定義 AOP 前置通知 |
aop : config | 頂層的 AOP 配置元素,大多數 aop : * 元素必須包含在 元素內 |
aop : declare-parents | 為被通知的物件引入額外介面,並透明的實現 |
aop : pointcut | 定義切點 |
參考來源:http://blog.csdn.net/github_34889651/article/details/51321499