Spring AOP的設計原理

前言

Spring 提供了AOP(Aspect Oriented Programming) 的支持, 那麼,什麼是AOP呢?本文將經過一個另一個角度來詮釋AOP的概念,幫助你更好地理解和使用Spring AOP。java

 

讀完本文,你將瞭解到:
1.    Java程序運行在JVM中的特徵
2.    Java程序的執行流【瞭解AOP、鏈接點(Join Point)、切入點(point cut)   的概念 】
3.    引入了代理模式的Java程序執行流(AOP實現的機制)
4.    Spring AOP的工做原理

 

 

 

1. Java程序運行在JVM中的特徵

 

         當咱們在某個類Foo中寫好了一個main()方法,而後執行java Foo,你的Java程序之旅就開啓了,以下:web

 

[java]  view plain  copy
 
 
 
 
  1. public class Foo {  
  2.     public static void main(String[] args) {  
  3.         // your codes begins here  
  4.     }  
  5.   
  6. }  

那麼在這個執行的過程當中,JVM都爲你幹了什麼呢?spring

當你執行java Foo 的時候,JVM會建立一個主線程main,這個主線程以上述的main()方法做爲入口,開始執行你的代碼。每個線程在內存中都會維護一個屬於本身的棧(Stack),記錄着整個程序執行的過程。棧裏的每個元素稱爲棧幀(Stack Frame),棧幀表示着某個方法調用,會記錄方法調用的信息;實際上咱們在代碼中調用一個方法的時候,在內存中就對應着一個棧幀的入棧和出棧。編程

 

關於虛擬機線程棧(JVM Stack)
關於虛擬機線程棧(JVM Thread Stack)的模型不是本文的重點,因此就不此處展開,讀者能夠查看個人另一篇博文,這裏面有很是詳盡的介紹 。    《Java虛擬機原理圖解》三、JVM運行時數據區 ]

 

在某個特定的時間點,一個Main線程內的棧會呈現以下圖所示的狀況:設計模式

從線程棧的角度來看,咱們能夠看到,JVM處理Java程序的基本單位是方法調用。實際上,JVM執行的最基本單位的指令(即原子操做)是彙編語言性質的機器字節碼。這裏之因此講方法調用時Java程序的基本執行單位,是從更宏觀的角度看待的。框架

 

如何獲取到虛擬機線程棧中的內容(即方法調用過程)?

   試想一下,如何可以獲取到JVM線程棧中的方法調用的內容 我相信全部的Java programmer都知道這個答案。Java Programmer幾乎天天都能看到------當咱們的代碼拋出異常而未捕獲或者運行時出現了Error錯誤時,咱們會受到一個很是討厭的Log信息,以下:ui

 



     固然,除了代碼拋出異常外,咱們仍是能夠其餘方式察覺JVM線程棧內的內容能夠經過Thread.dumpStack()方法建立一個假的Exception實例,而後將這個Exception實例記錄的當前線程棧的內容輸出到標準錯誤流中。例如我在某處代碼裏執行了Thread.dumpStack()方法,輸出了以下的結果:



 

 

2.  Java程序執行流 【瞭解AOP、鏈接點(Join Point)、切入點(point cut)   的概念 】

            若是從虛擬機線程棧的角度考慮Java程序執行的話,那麼,你會發現,真個程序運行的過程就是方法調用的過程。咱們按照方法執行的順序,將方法調用排成一串,這樣就構成了Java程序流。
lua

 

咱們將上述的線程棧裏的方法調用按照執行流排列,會有以下相似的圖:url

基於時間序列,咱們能夠將方法調用排成一條線。而每一個方法調用則能夠當作Java執行流中的一個節點。這個節點在AOP的術語中,被稱爲Join Point,即鏈接點。 一個Java程序的運行的過程,就是若干個鏈接點鏈接起來依次執行的過程。spa

在咱們正常的面向對象的思惟中, 咱們考慮的是如何按照時間序列經過方法調用來實現咱們的業務邏輯。那麼,什麼是AOP(即面向切面的編程)呢?

一般面向對象的程序,代碼都是按照時間序列縱向展開的,而他們都有一個共性:即都是已方法調用做爲基本執行單位展開的。 將方法調用當作一個鏈接點,那麼由鏈接點串起來的程序執行流就是整個程序的執行過程。

AOP(Aspect Oriented Programming)則是從另一個角度來考慮整個程序的,AOP將每個方法調用,即鏈接點做爲編程的入口,針對方法調用進行編程。從執行的邏輯上來看,至關於在以前縱向的按照時間軸執行的程序橫向切入。至關於將以前的程序橫向切割成若干的面,即Aspect.每一個面被稱爲切面。
因此,根據個人理解,AOP本質上是針對方法調用的編程思路。

     既然AOP是針對切面進行的編程的,那麼,你須要選擇哪些切面(即 鏈接點Joint Point)做爲你的編程對象呢?

   由於切面本質上是每個方法調用,選擇切面的過程實際上就是選擇方法的過程。那麼,被選擇的切面(Aspect)在AOP術語裏被稱爲切入點(Point Cut).  切入點實際上也是從全部的鏈接點(Join point)挑選本身感興趣的鏈接點的過程。

Spring AOP框架中經過 方法匹配表達式來表示切入點(Point Cut),至於詳細的表達式語法是什麼 不是本文的重點,請讀者自行參考Spring相應的說明文檔。

既然AOP是針對方法調用(鏈接點)的編程, 如今又選取了你感興趣的本身感興趣的連接點---切入點(Point Cut)了,那麼,AOP能對它作什麼類型的編程呢?AOP能作什麼呢? 

瞭解這個以前,咱們先要知道一個很是重要的問題: 既然AOP是對方法調用進行的編程,那麼,AOP如何捕獲方法調用的呢? 弄清楚這個問題,你不得不瞭解設計模式中的代理模式了。下面咱們先來了解一下引入了代理模式的Java程序執行流是什麼樣子的。

 

3.    引入了代理模式的Java程序執行流(AOP實現的機制)

 

咱們假設在咱們的Java代碼裏,都爲實例對象經過代理模式建立了代理對象,訪問這些實例對象必需要經過代理,那麼,加入了proxy對象的Java程序執行流會變得稍微複雜起來。
咱們來看下加入了proxy對象後,Java程序執行流的示意圖:


 

 

由上圖能夠看出,只要想調用某一個實例對象的方法時,都會通過這個實例對象相對應的代理對象, 即執行的控制權先交給代理對象。

關於代理模式
代理模式屬於Java代碼中常常用到的、也是比較重要的設計模式。代理模式能夠爲某些對象除了實現自己的功能外,提供一些額外的功能,大體做用以下圖所示:

關於代理模式的詳細介紹和分析,請參考個人另外一篇博文:

Java動態代理機制詳解(JDK 和CGLIB,Javassist,ASM)

 

加入了代理模式的Java程序執行流,使得全部的方法調用都通過了代理對象。對於Spring AOP框架而言,它負責控制着真個容器內部的代理對象。當咱們調用了某一個實例對象的任何一個非final的public方法時,整個Spring框架都會知曉。

此時的SpringAOP框架在某種程度上扮演着一個上帝的角色:它知道你在這個框架內所作的任何操做,你對每個實例對象的非final的public方法調用均可以被框架察覺到!

既然Spring代理層能夠察覺到你所作的每一次對實例對象的方法調用,那麼,Spring就有機會在這個代理的過程當中插入Spring的本身的業務代碼。

 

 

4.     Spring AOP的工做原理

 

前面已經介紹了AOP編程首先要選擇它感興趣的鏈接點----即切入點(Point cut),那麼,AOP能對切入點作什麼樣的編程呢? 咱們先將代理模式下的某個鏈接點細化,你會看到以下這個示意圖所表示的過程:

爲了下降咱們對Spring的AOP的理解難度,我在這裏將代理角色的職能進行了簡化,方便你們理解。(注意:真實的Spring AOP的proxy角色扮演的只能比這複雜的多,這裏只是簡化,方便你們理解,請不要先入爲主)代理模式的代理角色最起碼要考慮三個階段:

1. 在調用真正對象的方法以前,應該須要作什麼?

2. 在調用真正對象的方法過程當中,若是拋出了異常,須要作什麼?

3.在調用真正對象的方法後,返回告終果了,須要作什麼?

AOP對這個方法調用的編程,就是針對這三個階段插入本身的業務代碼。

 

如今咱們假設當前RealSubject這個角色的類是 org.luanlouis.springlearning.aop.FooService ,當前這個鏈接點對應的方法簽名是:public void foo()。那麼上述的代理對象的三個階段將會有如下的處理邏輯:            

1. 在調用真正對象的方法以前

      proxy會告訴Spring AOP:  "我將要調用類org.luanlouis.springlearning.aop.FooService  的public void foo() ,在調用以前,你有什麼處理建議嗎?";

      Spring AOP這時根據proxy提供的類名和方法簽名,而後拿這些信息嘗試匹配是否在其感興趣的切入點內,若是在感興趣的切入點內,Spring AOP會返回 MethodBeforeAdvice處理建議,告訴proxy應該執行的操做;

2. 在調用真正對象的方法過程當中,若是拋出了異常,須要作什麼?

      proxy告訴Spring AOP: 「我調用類org.luanlouis.springlearning.aop.FooService  的public void foo()過程當中拋出了異常,你有什麼處理建議?」

     Spring AOP根據proxy提供的類型和方法簽名,肯定了在其感興趣的切入點內,則返回相應的處理建議ThrowsAdvice,告訴proxy這個時期應該採起的操做。

3.在調用真正對象的方法後,返回告終果了,須要作什麼?

    proxy告訴Spring AOP:"我調用類org.luanlouis.springlearning.aop.FooService  的public void foo()結束了,並返回告終果你如今有什麼處理建議?";

    Spring AOP 根據proxy提供的類型名和方法簽名,肯定了在其感興趣的切入點內,則返回AfterReturingAdivce處理建議,proxy獲得這個處理建議,而後執行建議;

上述的示意圖中已經明確代表了Spring AOP應該作什麼樣的工做:根據proxy提供的特定類的特定方法執行的特定時期階段給出相應的處理建議。要完成該工做,Spring AOP應該實現:

1.肯定本身對什麼類的什麼方法感興趣? -----即肯定 AOP的切入點(Point Cut),這個能夠經過切入點(Point Cut)表達式來完成;

2. 對應的的類的方法的執行特定時期給出什麼處理建議?------這個須要Spring AOP提供相應的建議 ,即咱們常說的Advice。

 

到此爲止,AOP的基本工做機制已經介紹完畢了,我再次強調,上午我將Proxy方法的不一樣執行時期簡單的拆成了三個,是爲了方便你們理解AOP的工做機制,實際的AOP proxy的實現比這複雜的多。具體proxy是如何實現的,我將另外介紹,感興趣的話,敬請關注我後續的博文。

     《Spring設計思想》Spring AOP實現原理

相關文章
相關標籤/搜索