spring心得7--spring第二大特點AOP(面向切面)講解

y_keven發表於2013-05-07

    本篇部落格重點講解AOP的概念和知識點的介紹,下一篇部落格重點通過案例和註釋來分析講解spring aop的通知等概念

1.定義AOP術語

  1).切面(aspect):要實現的交叉功能,是系統模組化的一個切面或領域。如日誌記錄、事務處理、安全機制操作

  2).連線點:應用程式執行過程中插入切面的地點,可以是方法呼叫,異常丟擲,或者要修改的欄位。

  3).通知:切面的實際實現,他通知系統新的行為。如在日誌通知包含了實現日誌功能的程式碼,如向日志檔案寫日誌。通知在連線點插入到應用系統中。

  4).切入點:定義了通知應該應用在哪些連線點,通知可以應用到AOP框架支援的任何連線點。

  5).引入:為類新增新方法和屬性。

  6).目標物件:被通知的物件。既可以是你編寫的類也可以是第三方類。

  7).代理:將通知應用到目標物件後建立的物件,應用系統的其他部分不用為了支援代理物件而改變。

  8).織入:將切面應用到目標物件從而建立一個新代理物件的過程。織入發生在目標物件生命週期的多個點上:

  編譯期:切面在目標物件編譯時織入.這需要一個特殊的編譯器.

  類裝載期:切面在目標物件被載入JVM時織入.這需要一個特殊的類載入器.

  執行期:切面在應用系統執行時織入.

2.SpringAOP實現

    用java編寫spring通知;在spring中所有的通知都是以java類的形式編寫的。切入點定義在配置檔案中編寫,所以切面程式碼和配置檔案對我們來說都很熟悉。對於其他框架(Aspectj),需要特定的語法編寫,如果使用的話,還需學習新的語言。

   spring的執行時通知物件

   spring在執行期建立代理,不需要特殊的編譯器. spring有兩種代理方式:

   1).若目標物件實現了若干介面,spring使用JDK動態代理,即JDK的java.lang.reflect.Proxy類代理,該類讓spring動態產生一個新類,它實現了所需的介面,織入了通知,並且代理對目標物件的所有請求。

   2).若目標物件沒有實現任何介面,spring使用CGLIB代理,即使用cglib庫生成目標物件的子類。使用該方式時需要注意:對介面建立代理優於對類建立代理,因為會產生更加鬆耦合的系統。對類代理是讓遺留系統或無法實現介面的第三方類庫同樣可以得到通知,這種方式應該是備用方案標記為final的方法不能夠被通知。spring是為目標類產生子類。任何需要被通知的方法都被複寫,將通知織入。final方法是不允許重寫的

    spring實現了aop聯盟介面

    spring只支援方法連線點,不提供屬性接入點;spring的觀點是屬性攔截破壞了封裝。物件導向的概念是物件自己處理工作,其他物件只能通過方法呼叫的得到的結果。

3.建立通知

通知型別

介面

描述

Around

環繞通知

Org.springframework.aop.

MethodInterceptor

攔截對目標方法呼叫

Before

前置通知

Org.springframework.aop.

BeforeAdvice

在目標方法呼叫前呼叫

After

後置通知

Org.springframework.aop.

AfterReturningAdvice

在目標方法呼叫後呼叫

Throws

異常通知

Org.springframework.aop.

ThrowsAdvice

當目標方法丟擲異常時呼叫

   前置通知

   public interface MethodBeforeAdvice{

       void before(Method m,Object[] os ,Object target){

        }

   }

    該介面提供了獲得目標方法、引數和目標物件的機會。不能夠改變執行時引數,即不能替換引數物件和目標物件。

    注意在方法結束後不返回任何值東西。原因是該介面返回後,目標方法將會被呼叫,應該返回目標物件的返回值。該介面唯一能;阻止目標方法被呼叫的途徑是丟擲異常或(System.exit())。

    ProxyFactoryBean是一個在BeanFactory中顯式建立代理物件的中心類,可以給它一個要實現的介面、一個要代理的目標物件、一個要織入的通知,並且他將建立一個嶄新的代理物件

  後置通知

  同前置通知類似。

  public interface AfterReturningAdvice{

      public void afterReturning(Object returnValue,Method

      m,Object[] os,Object target);

  }

 環繞通知

public interface MethodInterceptor extends Interceptor{

    Object invoke(MethodInvocation invocation);

}

    該介面同前兩種通知有兩個重要區別:

    1).該通知能夠控制目標方法是否真的被呼叫。通過invocation.proceed()方法來呼叫;

    2).該通知可以控制返回的物件。可以返回一個與proceed()方法返回物件完全不同的物件。但要謹慎使用。

   異常通知

public interface ThrowsAdvice{}

    該介面為標識性介面,沒有任何方法,但實現該介面的類必須要有如下形式的方法:

void afterThrowing(Throwable throwable);

void afterThrowing(Method m,Object[] os,Object target,Throwable throwable);

第一個方法只接受一個引數:需要丟擲的異常。

第二個方法接受異常、被呼叫的方法、引數以及目標物件。

   引入通知

   以前定義的通知型別是在目標物件的方法被呼叫的周圍織入。引入通知給目標物件新增新的方法和屬性。

4.定義切入點

    如果不能表達在應用系統的什麼地方應用通知的話,通知將毫無用處,這就是切入點的用處。切入點決定了一個特定的類的特定方法是否滿足一定的規則。若符合,通知就應用到該方法上。

  在spring中定義切入點

  public interface Pointcut{

      ClassFilter getClassFilter();

      MethodMatcher getMethodMatcher();

  }

  切入點根據方法和類決定何處織入通知。ClassFilter介面決定了一個類是否符合通知的要求:

  public interface ClassFilter{

      boolean matches(Class clazz);

  }

    實現該介面的類決定了以引數傳遞進來的類是否應該被通知。實現該介面的類一般根據類名決定,但不一定必須如此。該介面總是包含了一個簡單的ClassFilter介面實現-ClassFilter.TRUTE。它是規範的適合任何類的ClassFilter例項,適合用於只根據方法決定時候符合要求的切入。ClassFilter通過類過濾切面,MethodMatcher通過方法過濾切面。

  public interface MethodMatcher{

       boolean matches(Method m,Class targetClass);1.

       boolean isRuntime();2.

       boolean matchers(Method m,Class target,Object[] args);3.

  }

   1根據目標類和方法決定方法是否被通知。因為可以靜態的判斷,所以可以在AOP代理被建立時候呼叫一次這個方法。該方法的結果最終決定了通知是否被織入。如果1.返回true;2.被呼叫來決定MethodMatcher的型別。有兩種型別:靜態和動態。靜態切入點的意思是通知總是被執行。如果一個切入點是靜態的,該方法返回false.動態切入點根據執行時方法的引數值決定通知是否需要執行。如果切入點是動態的,該方法返回true。和1.方法類似,該方法也是在代理建立時執行一次。如果切入點是靜態的,3.永遠不會執行,對於動態切入點,需要根據執行時的引數決定方法是否被通知,所以會增加系統的負擔,儘量使用靜態切入點。

5.理解Advisor

   大多數切面是由定義切面行為的通知和定義切面在什麼地方執行的切入點組合而成的。spring認識到了這一點,提供了Advisor類。他把通知和切入點組合到一個物件中。更確切地說PointcutAdvisor提供了這些功能。

  public interface PointcutAdvisor{

      Pointcut getPointcut();

      Advice getAdvice();

  }

    這樣方便在一個地方定義切入點和通知。

    使用spring的靜態切入點

    靜態切入點只在代理建立的時候執行一次而不是在執行期間每次方法呼叫都執行,所以效能比動態切入點好,是首選的切入點方式。spring為建立靜態切入點提供了方便的父類。staticMethodMatcherPointcut. StaticMethodMatcherPointcut

NameMatchMethodPointcut:

  public void setMappedName(String);

  public void setMappedNames(String);

  當呼叫的方法名字與給出的對映名字匹配時,切點才匹配。也可在名字的開始和結束使用萬用字元。定義切入點

  正規表示式切入點:RegexpMethodPointcut

符號

描述

示例

匹配

不匹配

.

匹配任何單個字元

setFoo.

setFooB

setFoo setFooBar

+

匹配前一個字元一次或多次

setFoo.+

setFooBar setFooB

setFoo

*

匹配前一個字元0次或多次

setFoo.*

setFoo setFooB, setFooBar

 

\

匹配任何正規表示式符號

\.setFoo.

bar.setFoo

setFoo

    謹慎使用引入通知

    spring通知是在執行時織入,而不象其他的AOP框架將通知織入到類的位元組碼當中。這意味著,從其他方法建立或得道的物件不會被引入通知。例如直接例項化或返序列化的物件。

屬性

使用

target

代理的目標物件

proxyInterfaces

代理應該實現的介面列表

interceptorNames

需要應用到目標物件上的通知的名字。可以是攔截器、Advisor或其他通知型別的名字。這個屬性必須按照beanFactory中的使用順序設定。

singleton

是否返回同一個例項。

aopProxyFactory

是用的ProxyFactoryBean實現。spring有兩種實現:jdk動態代理和CGLIB。通常不需要使用該屬性。

exposeProxy

目標物件是否需要得到當前代理。

frozen

一旦工廠被建立,是否可以修改代理的通知。當為true時,執行時就不能修改ProxyFactoryBean了。通常不需要使用這個屬性。

optimize

是否對建立的代理進行優化(只適用於CGLIB)。這會帶來效能的提升,不過要慎用。

ProxyTargetClass

是否代理目標物件,而不是介面。只能在使用CGLIB

(即部署了cglib包)時使用。

 

相關文章