面向切面的 Spring —— 什麼是面向切面程式設計?
Q1:面向切面程式設計(AOP)解決什麼問題?
A1:把橫切關注點與業務邏輯相分離。
Q2:什麼是橫切關注點?
A2:在軟體開發中,散佈於應用中多處的功能。
日誌是應用切面的常見範例,但並不是切面適用的唯一場景。
DI 有助於應用物件之間的解耦,而 AOP 可以實現橫切關注點與它們所影響的物件之間的解耦。
上圖展示了一個被劃分為模組的典型應用。每個模組的核心功能都是為特定業務領域提供服務,但是這些模組都需要類似的輔助功能,例如安全和事務管理。
如果要重用通用功能的話,最常見的面向物件技術是繼承或委託。
Q1:為什麼要使用切面來取代繼承和委託?
A1:如果在整個應用中都使用相的基類,繼承
Q2:什麼是切面?
A2:*橫切關注點被模組化為特殊的類*,這些類被稱為切面。
Q3:使用面向切面程式設計的好處?
A3:①、現在每個關注點都集中於一個地方,而不是分散到多處程式碼中;②、服務模組更簡潔,因為它們只包含主要關注點(或核心功能)的程式碼,而次要關注點的程式碼被轉移到切面中了。
Q:AOP 術語有哪些?
A:描述切面的常用術語有通知(advice)、切點(pointcut)和連線點(join point)。
&通知(Advice)
在 AOP 術語中,切面的工作被成為通知。
Spring 切面可以應用 5 種類型的通知:
- ①、前置通知(Before):在目標方法被呼叫之前呼叫通知功能;
- ②、後置通知(After):在目標方法完成之後通用通知,此時不會關心方法的輸出是什麼;
- ③、返回通知(After-returning):在目標方法成功執行之後呼叫通知;
- ④、異常通知(After-throwing):在目標方法丟擲異常後呼叫通知;
- ⑤、環繞通知(Around):通知包裹了被通知的方法,在被通知的方法呼叫之前和呼叫之後執行自定義的行為。
&連線點(Join point)
我們的應用可能也有數以千計的時機應用通知。這些時機被稱為連線點。連線點是在應用執行過程中能夠插入切面的一個點。這個點可以是呼叫方法時、丟擲異常時、甚至修改一個欄位。切面程式碼可以利用這些點插入到應用的正常流程之中,並新增新的行為。
&切點(Pointcut)
一個切面並不需要通知應用的所有連線點。切點有助於縮小切面所通知的連線點的範圍。 切點的定義會匹配通知所要織入的一個或多個連線點。我們通常使用明確的類和方法名稱,或是利用正則表示式定義所匹配的的類和方法名稱來指定這些切點。
&切面(Aspect)
切面是通知和切點的結合。通知和切點共同定義了切面的全部內容——它是什麼,在何時和何處完成其功能。
&引入(Introduction)
引入允許我們向現有的類新增新方法或屬性。
&織入(Weaving)
織入是把切面應用到目標物件並建立新的代理物件的過程。切面在指定的連線點被織入到目標物件中。在目標物件的生命週期裡有多個點可以進行織入:
- 編譯期:切面在目標類編譯時織入。這種方式需要特殊的編譯器。AspectJ 的織入編譯器就是以這種方法織入切面的。
- 類載入期:切面在目標類載入到 JVM 時被織入。這種方式需要特殊的類載入器,它可以在目標類被引入應用之前增強該目標類的位元組碼。AspectJ 5 的載入時織入就是支援以這種方式織入切面。
- 執行期:切面在應用執行的某個時刻被織入。一般情況下,在織入切面時,AOP 容器會為目標物件動態地建立一個代理物件。Spring AOP 就是以這種方式織入切面的。
現在我們已經瞭解瞭如下知識:通知包含了需要用於多個應用物件的橫切行為;連線點是程式執行過程中能夠應用通知的所有點;切點定義了通知被應用的具體位置(在哪些連線點)。關鍵的概念是切點定義了哪些連線點會得到通知。
Q:以上 AOP 的核心概念是如何在 Spring 中實現的?
AOP 框架的基本功能:建立切點來定義切面所織入的連線點。
Q1:Spring 提供了哪幾種類型的 AOP 支援?
A1:提供了以下四種類型:
- 基於代理的經典 Spring AOP;
- 純 POJO 切面;
- @AspectJ 註解驅動的切面;
- 注入式 AspectJ 切面(適用於 Spring 各版本)。
前三種都是 Spring AOP 實現的變體,Spring AOP 構建在動態代理基礎之上,因此,Spring 對 AOP 的支援侷限於方法攔截。
藉助 Spring 的 aop 名稱空間,我們可以將純 POJO 轉換為切面。實際上,這些 POJO 只是提供了滿足切點條件時所要呼叫的方法。但是需要 XML 配置。
Spring 借鑑了 AspectJ 的切面,以提供註解驅動的 AOP。本質上,它依然是 Spring 基於代理的 AOP,但是程式設計模型幾乎與編寫成熟的 AspectJ 註解切面完全一致。這種 AOP 風格的好處在於能夠不使用 XML 來完成功能。
如果你的 AOP 需求超過了簡單的方法呼叫(如構造器或屬性攔截),那麼你需要考慮使用 AspectJ 來實現切面。這種情況下,上文所示的第四種類型能夠幫助你將值注入到 AspectJ 驅動的切面中。
Q2:Spring AOP 框架有一些什麼關鍵知識?
A2:
①、Spring 通知是 Java 編寫的:我們可以使用與普通 Java 開發一樣的整合開發環境(IDE)來開發切面。而且,定義通知所應用的切點通常會使用註解或在 Spring 配置檔案裡採用 XML 來編寫。
②、Spring 在執行時通知物件:通過在代理類中包裹切面,Spring 在執行期把切面織入到 Spring 管理的 bean 中。代理類封裝了目標類,並攔截被通知方法的呼叫,再把呼叫轉發給真正的目標 bean。當代理攔截到方法呼叫時,在呼叫目標 bean 方法之前,會執行切面邏輯。直到應用需要被代理的 bean 時,Spring 才建立代理物件。因為 Spring 執行時才建立代理物件,所以我們不需要特殊的編譯器來織入 Spring AOP 的切面。
③、Spring 只支援方法級別的連線點:因為 Spring 基於動態代理,所以 Spring 只支援方法連線點。然而 AspectJ 和 JBoss 還支援欄位和構造器接入點。
Spring 缺少對欄位連線點的支援,無法讓我們建立細粒度的通知,例如攔截物件欄位的修改。
而且不支援構造器連線點,我們就無法在 bean 建立時應用通知。
但是方法攔截可以滿足絕大部分的需求,如果需要方法攔截之外的連線點攔截功能,那麼我們可以利用 AspectJ 來補充 Spring AOP 的功能。