天天看點

AOP 之 6.1 AOP基礎 ——跟我學spring3

6.1.1  AOP是什麼

        考慮這樣一個問題:需要對系統中的某些業務做日志記錄,比如支付系統中的支付業務需要記錄支付相關日志,對于支付系統可能相當複雜,比如可能有自己的支付系統,也可能引入第三方支付平台,面對這樣的支付系統該如何解決呢?

  • 傳統解決方案:

              1)日志部分提前公共類LogUtils,定義“longPayBegin”方法用于記錄支付開始日志,“logPayEnd”用于記錄支付結果:

AOP 之 6.1 AOP基礎 ——跟我學spring3

              2)支付部分,定義IPayService接口并定義支付方法“pay”,并定義了兩個實作:“PointPayService”表示積分支付,“RMBPayService”表示人民币支付;并且在每個支付實作中支付邏輯和記錄日志:

AOP 之 6.1 AOP基礎 ——跟我學spring3

              3)支付實作很明顯有重複代碼,這個重複很明顯可以使用模闆設計模式消除重複:

AOP 之 6.1 AOP基礎 ——跟我學spring3

4)到此我們設計了一個可以複用的接口;但大家覺得這樣記錄日志會很好嗎,有沒有更好的解決方案?

如果對積分支付方式添加統計功能,比如在支付時記錄下使用者總積分數、目前消費的積分數,那我們該如何做呢?直接修改源代碼添加日志記錄,這完全違背了面向對象最重要的原則之一:開閉原則(對擴充開放,對修改關閉)?

  • 更好的解決方案:在我們的支付元件中由于使用了日志元件,即日志子產品橫切于支付元件,在傳統程式設計中很難将日志元件分離出來,即不耦合我們的支付元件;是以面向方面程式設計AOP就誕生了,它能分離我們的元件,使元件完全不耦合:

1)采用面向方面程式設計後,我們的支付元件看起來如下所示,代碼中不再有日志元件的任何東西;

AOP 之 6.1 AOP基礎 ——跟我學spring3

2)是以日志相關的提取到一個切面中,AOP實作者會在合适的時候将日志功能織入到我們的支付元件中去,進而完全解耦支付元件和日志元件。

AOP 之 6.1 AOP基礎 ——跟我學spring3

看到這大家可能不是很了解,沒關系,先往下看。

面向方面程式設計(AOP):也可稱為面向切面程式設計,是一種程式設計範式,提供從另一個角度來考慮程式結構進而完善面向對象程式設計(OOP)。

       在進行OOP開發時,都是基于對元件(比如類)進行開發,然後對元件進行組合,OOP最大問題就是無法解耦元件進行開發,比如我們上邊舉例,而AOP就是為了克服這個問題而出現的,它來進行這種耦合的分離。

       AOP為開發者提供一種進行橫切關注點(比如日志關注點橫切了支付關注點)分離并織入的機制,把橫切關注點分離,然後通過某種技術織入到系統中,進而無耦合的完成了我們的功能。

6.1.2  能幹什麼

       AOP主要用于橫切關注點分離和織入,是以需要了解橫切關注點和織入:

  • 關注點:可以認為是所關注的任何東西,比如上邊的支付元件;
  • 關注點分離:将問題細化進而單獨部分,即可以了解為不可再分割的元件,如上邊的日志元件和支付元件;
  • 橫切關注點:一個元件無法完成需要的功能,需要其他元件協作完成,如日志元件橫切于支付元件;
  • 織入:橫切關注點分離後,需要通過某種技術将橫切關注點融合到系統中進而完成需要的功能,是以需要織入,織入可能在編譯期、加載期、運作期等進行。

橫切關注點可能包含很多,比如非業務的:日志、事務處理、緩存、性能統計、權限控制等等這些非業務的基礎功能;還可能是業務的:如某個業務元件橫切于多個子產品。如圖6-1

AOP 之 6.1 AOP基礎 ——跟我學spring3

圖6-1 關注點與橫切關注點

       傳統支付形式,流水方式:

AOP 之 6.1 AOP基礎 ——跟我學spring3

       面向切面方式,先将橫切關注點分離,再将橫切關注點織入到支付系統中:

AOP 之 6.1 AOP基礎 ——跟我學spring3

AOP能幹什麼:

  • 用于橫切關注點的分離和織入橫切關注點到系統;比如上邊提到的日志等等;
  • 完善OOP;
  • 降低元件和子產品之間的耦合性;
  • 使系統容易擴充;
  • 而且由于關注點分離進而可以獲得元件的更好複用。

6.1.3  AOP的基本概念

       在進行AOP開發前,先熟悉幾個概念:

  • 連接配接點(Jointpoint):表示需要在程式中插入橫切關注點的擴充點,連接配接點可能是類初始化、方法執行、方法調用、字段調用或處理異常等等,Spring隻支援方法執行連接配接點,在AOP中表示為“在哪裡幹”;
  • 切入點(Pointcut):選擇一組相關連接配接點的模式,即可以認為連接配接點的集合,Spring支援perl5正規表達式和AspectJ切入點模式,Spring預設使用AspectJ文法,在AOP中表示為“在哪裡幹的集合”;
  • 通知(Advice):在連接配接點上執行的行為,通知提供了在AOP中需要在切入點所選擇的連接配接點處進行擴充現有行為的手段;包括前置通知(before advice)、後置通知(after advice)、環繞通知(around advice),在Spring中通過代理模式實作AOP,并通過攔截器模式以環繞連接配接點的攔截器鍊織入通知;在AOP中表示為“幹什麼”;
  • 方面/切面(Aspect):橫切關注點的子產品化,比如上邊提到的日志元件。可以認為是通知、引入和切入點的組合;在Spring中可以使用Schema和@AspectJ方式進行組織實作;在AOP中表示為“在哪幹和幹什麼集合”;
  • 引入(inter-type declaration):也稱為内部類型聲明,為已有的類添加額外新的字段或方法,Spring允許引入新的接口(必須對應一個實作)到所有被代理對象(目标對象), 在AOP中表示為“幹什麼(引入什麼)”;
  • 目标對象(Target Object):需要被織入橫切關注點的對象,即該對象是切入點選擇的對象,需要被通知的對象,進而也可稱為“被通知對象”;由于Spring AOP 通過代理模式實作,進而這個對象永遠是被代理對象,在AOP中表示為“對誰幹”;
  • AOP代理(AOP Proxy):AOP架構使用代理模式建立的對象,進而實作在連接配接點處插入通知(即應用切面),就是通過代理來對目标對象應用切面。在Spring中,AOP代理可以用JDK動态代理或CGLIB代理實作,而通過攔截器模型應用切面。
  • 織入(Weaving):織入是一個過程,是将切面應用到目标對象進而建立出AOP代理對象的過程,織入可以在編譯期、類裝載期、運作期進行。

在AOP中,通過切入點選擇目标對象的連接配接點,然後在目标對象的相應連接配接點處織入通知,而切入點和通知就是切面(橫切關注點),而在目标對象連接配接點處應用切面的實作方式是通過AOP代理對象,如圖6-2所示。

AOP 之 6.1 AOP基礎 ——跟我學spring3

圖6-2 概念關系

接下來再讓我們具體看看spring有哪些通知類型:

  • 前置通知(Before Advice):在切入點選擇的連接配接點處的方法之前執行的通知,該通知不影響正常程式執行流程(除非該通知抛出異常,該異常将中斷目前方法鍊的執行而傳回)。
  • 後置通知(After Advice):在切入點選擇的連接配接點處的方法之後執行的通知,包括如下類型的後置通知:
    • 後置傳回通知(After returning Advice):在切入點選擇的連接配接點處的方法正常執行完畢時執行的通知,必須是連接配接點處的方法沒抛出任何異常正常傳回時才調用後置通知。
    • 後置異常通知(After throwing Advice): 在切入點選擇的連接配接點處的方法抛出異常傳回時執行的通知,必須是連接配接點處的方法抛出任何異常傳回時才調用異常通知。
    • 後置最終通知(After finally Advice): 在切入點選擇的連接配接點處的方法傳回時執行的通知,不管抛沒抛出異常都執行,類似于Java中的finally塊。
  • 環繞通知(Around Advices):環繞着在切入點選擇的連接配接點處的方法所執行的通知,環繞通知可以在方法調用之前和之後自定義任何行為,并且可以決定是否執行連接配接點處的方法、替換傳回值、抛出異常等等。

各種通知類型在UML序列圖中的位置如圖6-3所示:

AOP 之 6.1 AOP基礎 ——跟我學spring3

圖6-3 通知類型

6.1.4  AOP代理

       AOP代理就是AOP架構通過代理模式建立的對象,Spring使用JDK動态代理或CGLIB代理來實作,Spring預設使用JDK動态代理來實作,進而任何接口都可别代理,如果被代理的對象實作不是接口将預設使用CGLIB代理,不過CGLIB代理當然也可應用到接口。

       AOP代理的目的就是将切面織入到目标對象。

       概念都将完了,接下來讓我們看一下AOP的 HelloWorld!吧。

原創内容 轉自請注明出處【http://sishuok.com/forum/blogPost/list/2466.html】

繼續閱讀