1. 程式人生 > >Spring入門之面向切面的Spring

Spring入門之面向切面的Spring

將橫切關注點與業務邏輯相分離。

散佈於應用中多處的相同功能被稱為橫切關注點,如日誌、安全和事務管理等。

有助於應用物件之間的解耦,而AOP可以實現橫切關注點與他們所影響的物件之間的解耦。

橫切關注點可以被模組化為特殊的類,這些類被稱為切面(aspect)。

  • a、每個關注點都集中於一個地方,而不是分散到多處程式碼中;

  • b、服務模組更簡潔,因為它們只包含核心功能的程式碼,次要關注點的程式碼被轉移到切面中了。

描述切面的常用術語有通知(advice)、切點(pointcut)和連線點(joinpoint)。

6.1、通知(Advice)

通知定義了切面是什麼以及何時使用。 Spring切面可以應用5種類型的通知:

  • a、前置通知(Before):

在目標方法被呼叫之前呼叫通知功能;

  • b、後置通知(After):

在目標方法完成之後呼叫通知,此時不會關心方法的輸出是什麼;

  • c、返回通知(After-returning):

在目標方法成功執行之後呼叫通知;

  • d、異常通知(After-throwing):

在目標方法丟擲異常後呼叫通知;

  • e、環繞通知(Around):

通知包裹了被通知的方法,在被通知的方法呼叫之前和呼叫之後執行自定義的行為。

6.2連線點(Join point)

我們的應用有很多時機應用通知。這些時機被稱為連線點。連線點是在應用執行過程中能夠插入切面的一個點。這個點可以是呼叫方法時、丟擲異常時,甚至修改一個欄位時。切面程式碼可以利用這些點插入到應用的正常流程之中,並新增新的行為。

6.3切點(Poincut)

如果讓一位抄表員訪問電力公司所服務的所有住戶,那肯定是不現實的。實際上,電力公司為每一個抄表員都分別指定某一塊區域的住戶。類似地,一個切面並不需要通知應用的所有連線點。切點有助於縮小切面所通知的連線點範圍。 如果說通知定義了切面的“什麼”和“何時”的話,那麼切點就定義了“何處”。切點的定義會匹配通知所要織入的一個或多個連線點。我們通常使用明確的類和方法名稱,或是利用正則表示式定義所匹配的類和方法名稱來指定這些切點。有些AOP框架允許我們建立動態的切點,可以根據執行時的決策(比如方法的引數值)來決定是否應用通知。

6.4切面(Aspect)

當抄表員開始一天的工作時,他知道自己要做的事情(報告用電量)和從哪些房屋收集資訊。因此,他知道要完成工作所需要的一切東西。切面是通知和切點的結合。通知和切點共同定義了切面的全部內容——它是什麼,在何時和何處完成其功能。

6.5引入(Introduction)

引入允許我們向現有的類新增新方法或屬性。例如,我們可以建立一個Auditable通知類,該類記錄了物件最後一次修改時的狀態。這很簡單,只需要一個方法,setLastModified(Date),和一個例項變數來儲存這個狀態。然後,這個新方法和例項變數就可以被引入到現有的類中,從而可以在無需修改這些現有的類的情況下,讓它們具有新的行為和狀態。

6.6織入(Weaving)

織入是把切面應用到目標物件並建立新的代理物件的過程。切面在指定的連線點被織入到目標物件中。在目標物件的生命週期裡有多個點可以進行織入:

  • a、編譯期:切面在目標類編譯時別織入。這種方式需要特殊的編譯器。AspectJ的織入編譯器就是以這種方式織入切面的。

  • b、類載入期:切面在目標類載入到JVM時被織入。這種方式需要特殊的類載入器,它可以在目標類被引入應用之前增加該目標類的位元組碼。AspectJ5的載入時織入就支援這種方式。

  • c、執行期:切面在應用執行的某個時候被織入。一般情況下,在織入切面時,AOP容器會為目標物件動態地建立一個代理物件。Spring AOP就是以這種方式織入切面的。

通知包含了需要用於多個應用物件的橫切行為;連線點是程式執行過程中能夠應用通知的所有點;切點定義了通知被應用的具體位置(在哪些連線點)。其中關鍵的概念是切點定義了哪些連線點會得到通知。

  • a、基於代理的經典Spring AOP;

  • b、純POJO切面;

  • c、@AspectJ註解驅動的切面;

  • d、注入時AspectJ切面(適用於Spring各版本);

SpringAOP構建在動態代理基礎上,因此,Spring對AOP的支援侷限於方法攔截。Spring經典的AOP過於繁雜和笨重(直接使用ProxyFactory Bean會讓人感覺厭煩),二宣告式AOP和基於註解的AOP更為簡單。

實際上,這些POJO只是提供了滿足切點條件時所要呼叫的方法。遺憾的是,這種技術需要XML配置,但這的確是宣告式地將物件轉換為切面的簡便方式(專案中可以考慮混合配置);基於@AspectJ註解驅動的AOP,本質上仍然是基於代理的AOP,但是程式設計模型幾乎與編寫成熟的AspectJ註解切面完全一致。這種AOP風格的好處在於能夠不使用XML來完成功能。

如果你的AOP需求超過了簡單的方法呼叫(如構造器和屬性攔截),那麼你需要考慮使用AspectJ來實現切面。

Spring所建立的通知都是使用標準的Java類編寫的,定義通知所應用的切點通常會使用註解或在Spring配置檔案裡採用XML來編寫,這兩種語法對Java開發者來說都是相當熟悉的。 AspectJ與之相反。雖然AspectJ現在支援基於註解的切面,但AspectJ最初是以Java語言擴充套件的方式來實現的。這種方式有缺點也有優點。通過特有的AOP語言可以獲得更強大和細粒度的控制,以及更豐富的AOP工具集,但是我們需要額外學習新的工具和語法。

Spring執行時才建立代理物件,我們不需要特殊的編譯器來織入SpringAOP的切面。

Spring的切面由包裹了目標物件的代理類實現。代理類處理方法的呼叫,執行額外的切面邏輯,並呼叫目標方法。

Spring藉助AspectJ的切點表示式語言來定義Spring切面,且Spring僅支援部分AspectJ的指示器。其中,只有execution()指示器是實際執行匹配的,其他指示器都是用來限制匹配的。這說明execution指示器是我們在編寫切點定義時最主要使用的指示器,再次基礎上,我們使用其他指示器來限制所匹配的切點。

切點用於準確定位應該在什麼地方應用切面的通知。通知和切點是切面的最基本元素。

我們定義一個Performance介面來作為切點的目標:

Performance可以代表任何型別的現場表演,假設我們想編寫Performance的perform()方法觸發的通知。下面的表示式能夠設定當perform()方法執行時出發通知的呼叫。

我們使用execution()指示器選擇Performance的perform()方法。方法表示式以” * “號開始,表明了我們不關心方法返回值的型別。然後,我們制定了許可權定類名和方法名。對於方法引數列表,我們使用兩個點號(..)武漢英語培訓機構表明切點要選擇任意的perform()方法,無論該方法的入參是什麼。 如果我們要求配置的切點僅匹配concert包,那麼表示式應該是這樣:

and等同於&&,同理or、not等同於||和!。

除了AspectJ中的指示器外,Spring還引入了一種新的bean()指示器,它允許我們在切點表示式中使用bean的ID來表示bean。該指示器的一種典型應用是:在特定場景下,切面的通知會被編址到所有ID不為指定ID的bean中:

在此場景下,切面的通知會被編織到所有ID不為woodstock的bean中。這些就是編寫切點的基礎知識,接下來我們瞭解如何編寫通知和使用這些切點宣告切面。

在JavaConfig中啟用AspectJ註解的自動代理