《Spring3實戰》摘要(4-1)--面向切面的Spring

第4章 面向切面的 Spring

在軟件開發中,分佈於應用中多處的功能被稱爲橫切關注點(cross-cutting concerns)。通常,這些橫切關注點從概念上是與應用的業務邏輯相分離的(但是往往直接嵌入到應用的業務邏輯之中)。將這些橫切關注點與業務邏輯相分離正是面向切面編程(AOP)所要解決的。

依賴注入有助於應用對象之間的解耦,而AOP可以實現橫切關注點與它們所影響的對象之間的解耦。

4.1 什麼是面向切面編程

這裏寫圖片描述

在使用面向切面編程時,我們仍然在一處地方定義通用功能,但是我們可以通過聲明的方式定義這個功能以何種方式在何處應用,而無需修改受影響的類。

切面關注點可以被模塊化爲特殊的類,這些類被稱爲切面。這樣做有兩個好處:首先,每個關注點現在都集中於一處,而不是分散到多處代碼中;其次,服務模塊更簡潔,因爲它們只包含主要關注點(或核心功能)的代碼,而次要關注點的代碼被轉移到切面中了。

4.1.1 定義 AOP 術語

描述切面的常用術語有通知(advice)、切點(pointcut)和連接點(join point)。
這裏寫圖片描述

(1)通知(advice)
在AOP術語中,切面的工作被稱爲通知。

通知定義了切面是什麼以及何時使用。除了描述切面要完成的工作,通知還解決了何時執行這個工作的問題。

Spring切面可以應用5種類型的通知:

通知(advice)類型 說明
Before 在方法被調用之前調用通知。
After 在方法完成之後調用通知,無論方法執行是否成功。
After-returning 在方法成功執行之後調用通知。
After-throwing 在方法拋出異常後調用通知。
Around 通知包裹了被通知的方法,在被通知的方法調用之前和之後執行自定義的行爲。



(2)連接點(Joinpoint)
連接點是在應用執行過程中能夠插入切面的一個點。這個點可以是調用方法時、拋出異常時、甚至修改一個字段時。切面代碼可以利用這些點插入到應用的正常流程之中,並添加新的行爲。

(3)切點(Pointcut)
一個切面並不需要通知應用的所有連接點,切點有助於縮小切面所通知連接點的範圍。

如果通知定義了切面的“什麼”和“何時”,那麼切點就定義了“何處”。切點的定義會匹配通知所要織入的一個或多個連接點。我們通常使用明確的類和方法名稱來指定這些切點,或是利用正則表達式定義匹配的類和方法名稱模式來指定這些切點。有些AOP框架允許我們創建動態的切點,可以根據運行時的決策(比如方法的參數值)來覺得是否應用通知。

(4)切面(Aspect)
切面是通知和切點的結合。通知和切點共同定義了關於切面的全部內容—-它是什麼,在何時何處完成其功能。

(5)引入(Instroduction)
引入允許我們向現有的類添加新方法和屬性。例如,我們可以創建一個 Auditable 通知類,該類記錄了對像最後一次修改時的狀態。這很簡單,只需要一種方法 setLastModified(Date) ,和一個實例變量來保存這個狀態。然後,這個新方法和實例變量就可以被引入到現有的類中。從而可以在無需修改這些現有的類的情況下,讓它們具有新的行爲和狀態。

(6)織入(Weaving)
織入是將切面應用到目標對象來創建新的代理對象的過程。切面在指定的連接點被織入到目標對象中。在目標對象的生命週期裏有多個點可以進行織入。

  • 編譯期:切面在目標類編譯時被織入。這種方式需要特殊的編譯器。AspectJ的織入編譯器就是以這種方式織入切面的。
  • 類加載期:切面在目標類加載到 JVM 時被織入。這種方式需要特殊的類加載器(ClassLoader),它可以在目標類被引入應用之前增強該目標類的字節碼。
  • 運行期:切面在應用運行的某個時刻被織入。一般情況下,在織入切面時,AOP 容器會爲目標對象動態地創建一個代理對象。Spring AOP 就是以這種方式織入切面的。

4.1.2 Spring 對 AOP 的支持

三大 AOP 框架:

Spring提供了4種各具特色的AOP支持:

  • 基於代理的經典 AOP;
  • @AspectJ 註解驅動的切面
  • 純 POJO 切面
  • 注入式 AspectJ 切面(適合 Spring 各版本)。

前三種都是 Spring 基於代理的 AOP 變體,因此,Spring 對 AOP 支持侷限於方法攔截。如果 AOP 需求超過了簡單方法攔截的範疇(比如構造器或屬性攔截)。那麼應該考慮在 AspectJ 裏實現切面,利用 Spring 的DI(依賴注入)把 Spring Bean 注入到 AspectJ 切面中。

4.2 使用切點選擇連接點

Spring藉助AspectJ的切點表達式語言來定義Spring切面。下表列出了 Spring AOP 所支持的 AspectJ 切點指示器。在 Spring 中嘗試使用 AspectJ 其他指示器時,將會拋出 IllegalArgumentException 異常。

AspectJ指示器 描述
arg() 限制連接點匹配參數爲指定類型的執行方法
@args() 限制連接點匹配參數由指定註解標註的執行方法
execution() 用於匹配是連接點的執行方法
this() 限制連接點匹配AOP代理的Bean引用爲指定類型的類
target() 限制連接點匹配目標對象爲指定類型的類
@target() 限制連接點匹配特定的執行對象,這些對象對應的類要具備指定類型的註解
within() 限制連接點匹配指定的類型
@within() 限制連接點匹配指定註解所標註的類型(當使用 Spring AOP 時,方法定義在由指定的註解所標註的類裏)。
@annotation 限制匹配帶有指定註解的連接點


上表中展示的這些 Spring 支持的指示器,只有 execution 指示器是唯一的執行匹配,而其他的指示器都是用於限制匹配的。這說明 execution 指示器是我們在編寫切點定義時最主要使用的指示器。在此基礎上,我們使用其他指示器來限制所匹配的切點。

4.2.1 編寫切點(AspectJ切點表達式)

示例1:
這裏寫圖片描述

注:代表方法使用任意參數的符號爲兩點,在方法的括號中輸入三個點解析會出現錯誤。

示例2:
這裏寫圖片描述

4.2.2 使用 Spring 的 bean() 指示器

Spring 2.5 還引入了一個新的 bean() 指示器,該指示器允許我們在切點表達式中使用 Bean 的 ID 來標識 Bean。bean() 使用 Bean ID 或 Bean 名稱作爲參數來限制切點只匹配特定的 Bean。

<!-- 示例1:在執行Instrument的play()方法時應用通知,但限定 Bean ID 爲 eddie -->
exection(* com.springinaction.springidol.Instrument.play())
    and bean(eddie)
<!-- 示例2: -->
exection(* com.springinaction.springidol.Instrument.play())
    and !bean(eddie)
發佈了66 篇原創文章 · 獲贊 37 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章