1. 程式人生 > >Spring AOP:原理、 通知、連線點、切點、切面、表示式

Spring AOP:原理、 通知、連線點、切點、切面、表示式

0:Spring AOP 原理

簡單說說 AOP 的設計:

  1. 每個 Bean 都會被 JDK 或者 Cglib 代理。取決於是否有介面。

  2. 每個 Bean 會有多個“方法攔截器”。注意:攔截器分為兩層,外層由 Spring 核心控制流程,內層攔截器是使用者設定,也就是 AOP。

  3. 當代理方法被呼叫時,先經過外層攔截器,外層攔截器根據方法的各種資訊判斷該方法應該執行哪些“內層攔截器”。內層攔截器的設計就是職責連的設計。

可以將 AOP 分成 2 個部分:

第一:代理的建立;

第二:代理的呼叫。

開始分析(扯):

  1. 代理的建立(按步驟):

  • 首先,需要建立代理工廠,代理工廠需要 3 個重要的資訊:攔截器陣列,目標物件介面陣列,目標物件。

  • 建立代理工廠時,預設會在攔截器陣列尾部再增加一個預設攔截器 —— 用於最終的呼叫目標方法。

  • 當呼叫 getProxy 方法的時候,會根據介面數量大餘 0 條件返回一個代理物件(JDK or  Cglib)。

  • 注意:建立代理物件時,同時會建立一個外層攔截器,這個攔截器就是 Spring 核心的攔截器。用於控制整個 AOP 的流程。

  1. 代理的呼叫

  • 當對代理物件進行呼叫時,就會觸發外層攔截器。

  • 外層攔截器根據代理配置資訊,建立內層攔截器鏈。建立的過程中,會根據表示式判斷當前攔截是否匹配這個攔截器。而這個攔截器鏈設計模式就是職責鏈模式。

  • 當整個鏈條執行到最後時,就會觸發建立代理時那個尾部的預設攔截器,從而呼叫目標方法。最後返回。

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 種通知型別:
  1. Before——在方法呼叫之前呼叫通知
  2. After——在方法完成之後呼叫通知,無論方法執行成功與否
  3. After-returning——在方法執行成功之後呼叫通知
  4. After-throwing——在方法丟擲異常後進行通知
  5. Around——通知包裹了被通知的方法,在被通知的方法呼叫之前和呼叫之後執行自定義的行為
  • 連線點: 
    • 定義:連線點是一個應用執行過程中能夠插入一個切面的點。
    • 連線點可以是呼叫方法時、丟擲異常時、甚至修改欄位時、
    • 切面程式碼可以利用這些點插入到應用的正規流程中
    • 程式執行過程中能夠應用通知的所有點。
  • 切點: 
    • 定義:如果通知定義了“什麼”和“何時”。那麼切點就定義了“何處”。切點會匹配通知所要織入的一個或者多個連線點。
    • 通常使用明確的類或者方法來指定這些切點。
    • 作用:定義通知被應用的位置(在哪些連線點)
  • 切面: 
    • 定義:切面是通知和切點的集合,通知和切點共同定義了切面的全部功能——它是什麼,在何時何處完成其功能。
  • 引入: 
    • 引入允許我們向現有的類中新增方法或屬性
  • 織入: 
    • 織入是將切面應用到目標物件來建立的代理物件過程。
    • 切面在指定的連線點被織入到目標物件中,在目標物件的生命週期中有多個點可以織入
  1. 編譯期——切面在目標類編譯時期被織入,這種方式需要特殊編譯器。AspectJ的織入編譯器就是以這種方式織入切面。
  2. 類載入期——切面在類載入到
  3. JVM ,這種方式需要特殊的類載入器,他可以在目標類被引入應用之前增強該目標類的位元組碼。AspectJ5 的 LTW 就支援這種織入方式
  4. 執行期——切面在應用執行期間的某個時刻被織入。一般情況下,在織入切面時候,AOP 容器會為目標物件動態的建立代理物件。Spring AOP 就是以這種方式織入切面。

3:Spring 對 AOP 的支援

  • 並不是所有的 AOP 框架都是一樣的,他們在連線點模型上可能有強弱之分。 
    • 有些允許對欄位修飾符級別應用通知
    • 有些支援方法呼叫連線點
  • Spring 提供的 4 種各具特色的 AOP 支援
  1. 基於代理的經典 AOP;
  2. @AspectJ 註解驅動的切面;
  3. 純 POJO 切面;
  4. 注入式 AspectJ 切面; Spring
  • 在執行期間通知物件 
    • 通過在代理類中織入包裹切面,Spring 在執行期間將切面織入到 Spring 管理的 Bean 中。 
      代理類封裝了目標類,並攔截被通知的方法呼叫,再將呼叫轉發給真正的目標 Bean 

      Spring代理機制

       
      當攔截到方法呼叫時,在呼叫目標 Bean 方法之前,代理會執行切面邏輯。 
      當真正應用需要被代理的 Bean 時,Spring 才建立代理物件。如果使用 ApplicationContext,在 ApplicationContext 從 BeanFactory 中載入所有 Bean 時,Spring 建立代理物件,因為 Spring 在執行時候建立代理物件,所以我們不需要特殊的編譯器來織入 Spring AOP 的切面。
  • 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