Spring(四):面向切面編程AOP

  • 橫切關注點:分佈於應用中多處的功能java

  • 面向切面編程AOP:將橫切關注點與業務邏輯相分離正則表達式


    在使用面向切面編程時,仍在一個地方定義通用功能,可是能夠經過聲明的方式定義這個功能以何種方式在何處應用,而無需修改受影響的類。spring

    橫切關注點能夠被模塊化爲特殊的類,這些類被稱爲切面。
express

好處編程

  • 每一個關注點集中於一處,而不是分散到多處代碼中app

  • 服務模塊更加簡潔,由於它們只包含主要關注點的代碼,次要關注點被轉移到切面中了框架


一、定義AOP術語

1.一、通知(Advice)

    切面的工做被稱爲通知。
eclipse

    通知定義了切面是什麼以及什麼時候使用。
模塊化


Spring切面能夠應用的5種類型的通知:this

  • Before——在方法被調用以前調用通知

  • After——在方法完成以後調用通知,不管方法執行是否成功。

  • After-returning——在方法成功執行以後調用通知。

  • After-throwing——在方法拋出異常後調用通知。

  • Around——通知包裹了被通知的方法,在被通知的方法調用以前和調用以後執行自定義的行爲。

1.二、鏈接點(Joinpoint)

    鏈接點是在應用執行過程當中可以插入切面的一個點,這個店能夠是調用方法時、拋出異常時、甚至修改一個字段時。

1.三、切點(Pointcut)

    切點有助於縮小切面所通知鏈接點的範圍,切點的定義會匹配通知所要織入的一個或多個鏈接點。一般使用明確的類名和方法名來指定這些切點,或是利用正則表達式定義匹配的類和方法名來指定這些切點。

1.四、切面(Aspect)

    切面是通知和切點的結合,通知和切點共同定義了關於切面的所有內容——它是什麼、在什麼時候和何處完成其功能。

1.五、引入(Introduction)

    引入容許咱們向現有的類添加新方法或屬性。

1.六、織入(Weaving)

    織入是將切面應用到目標對象來建立新的代理對象的過程。

在目標對象的生命週期裏有多個點能夠進行織入:

  • 編譯期——切面在目標類編譯時被織入。這種方式須要特殊的編譯器。AspectJ的織入編譯器就是以這種方式織入切面的。

  • 類加載期——切面在目標類加載到JVM時被織入。

  • 運行期——切面在應用運行的某個時候被織入。通常狀況下,在織入切面時,AOP容器會爲目標對象動態地建立一個代理對象,Spring AOP就是以這種方式織入切面的。


二、Spring對AOP的支持

    並非全部AOP框架都是同樣的,有些容許對字段修飾符級別應用通知,而另外一些只支持與方法調用相關的鏈接點。它們在織入切面的方式和時機也有不一樣。但不管如何,建立切點來定義切面織入的鏈接點是AOP框架的基本功能。

    AOP世界三足鼎立格局:

  • AspectJ(http://eclipse.org/aspectj);

  • JBoss AOP(http://www.jboss.org/jbossaop);

  • Spring AOP(http://www.springframework.org)


    Spring提供了4種各具特點的AOP支持:

  • 基於代理的經典AOP;

  • @AspectJ註解驅動的切面;

  • 純POJO切面;

  • 注入式AspectJ切面(適合Spring各版本)。

    前3種都是Spring基於代理的AOP變體,所以,Spring對AOP的支持侷限於方法攔截。超過了簡單方法攔截的範疇,考慮在AspectJ裏面實現切面,利用Spring的DI把Spring Bean注入到AspectJ切面中。


    Spring AOP框架的關鍵點:

  • Spring通知是Java類編寫的,定義通知所應用的切點一般在Spring配置文件裏採用XML來編寫的。

  • Spring在運行期通知對象,代理類封裝了目標類,並攔截被通知的方法的調用,再將調用轉發給真正的目標Bean。當攔截到方法調用時,在調用目標Bean方法以前,代理會執行切面邏輯。

  • Spring只支持方法鏈接點,由於Spring基於動態代理,因此Spring只支持方法鏈接點。這與其餘AOP框架是不一樣的,例如AspectJ和Jboss,除了方法切點,它們還提供了字段和構造器接入點。


2.一、使用切點選擇鏈接點

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

    在Spring AOP中須要使用AspectJ的切點表達式語言來定義切點。關於Spring AOP的AspectJ切點,最重要的一點是Spring僅支持AspectJ切點指示器(pointcut designator)的一個子集。Spring是基於代理的,而某些切點表達式是與基於代理的AOP無關的。

Spring AOP 所支持的AspectJ切點指示器:

AspectJ指示器 描述
arg()
限制鏈接點匹配參數爲指定類型的執行方法
@arg() 限制鏈接點匹配參數由指定註解標註的執行方法
execution() 用於匹配是鏈接點的執行方法
this() 限制鏈接點匹配AOP代理的Bean引用爲指定類型的類
target() 限制鏈接點匹配目標對象爲指定類型的類
@target() 限制鏈接點匹配特定的執行對象,這些對象對應的類要具有指定類型的註解
within() 限制鏈接點匹配指定的類型
@within() 限制鏈接點匹配指定註解所標註的類型(當使用Spring AOP時,方法定義由指定的註解所標註的類裏)
@annotation 限制匹配帶有指定註解鏈接點

    在Spring中嘗試使用AspectJ其餘指示器時,將會拋出IllegalArgumentException異常。

    execution指示器是惟一的執行匹配,而其餘的指示器都用於限制匹配的,它是最主要使用的指示器。

2.1.一、編寫切點

使用AspectJ切點表達式來定位:

使用within()指示器來限制匹配:

    由於在xml中&有特殊含義,因此在使用Spring的基於xml配置來描述切點時,可使用and代替&&,or代替||和not代替!。

2.1.二、使用Spring的bean()指示器

    該指示器容許咱們在切點表達式中使用Bean的ID來標識Bean。

如:

    在執行Instrument的play()方法時應用通知,但限定Bean的ID爲eddie。

2.二、在xml中聲明切面

Spring的AOP配置元素:

AOP配置元素 描述
<aop:advisor> 定義AOP通知器
<aop:after> 定義AOP後置通知(無論被通知的方法是否執行成功)
<aop:after-returning> 定義AOP after-returning通知
<aop:after-throwing> 定義AOP after-throwing 通知
<aop:around> 定義AOP環繞通知
<aop:aspect> 定義切面
<aop:aspectj-autoproxy> 啓用@AspectJ註解驅動的切面
<aop:before> 定義AOP前置通知
<aop:config> 頂層的AOP配置元素,大多數<aop:*>元素必須包含在<aop:config>元素內
<aop:declare-parents> 爲被通知的對象引入額外的接口,並透明地實現
<aop:pointcut> 定義切點
2.2.一、聲明前置和後置通知
<aop:config>
    <!--引用audience Bean-->
    <aop:aspect ref="audience">
    
    <!--表演以前-->
    <aop:before pointcut="execution(* com.springinaction.springdol.Performer.perform(..))"
        method="takeSeats" />
        
    <!--表演以後-->
    <aop:after-returning pointcut="execution(* com.springinaction.springdol.Performer.perform(..))"
        method="applaud" />
    
    <!--表演失敗以後-->
    <aop:after-throwing pointcut="execution(* com.springinaction.springdol.Performer.perform(..))"
        method="demandRefund" />
</aop:config>

    大多數的AOP配置元素必須在<aop:config>元素的上下文內使用,在<aop:config>元素內,能夠聲明一個或多個通知器、切面或者切點。


    爲避免重複定義切點,可使用<aop:pointcut>元素定義一個命名切點:

<aop:config>
    <!--引用audience Bean-->
    <aop:aspect ref="audience">
    
        <!--定義切點-->
        <aop:pointcut id="performance" 
            expression="(* com.springinaction.springdol.Performer.perform(..))" />
        
        <!--表演以前-->
        <aop:before pointcut-ref="performance"
            method="takeSeats" />
            
        <!--表演以後-->
        <aop:after-returning pointcut-ref="performance"
            method="applaud" />
        
        <!--表演失敗以後-->
        <aop:after-throwing pointcut-ref="performance"
            method="demandRefund" />
    </aop:aspect>
</aop:config>
2.2.二、聲明環繞通知

    利用<aop:around>來聲明環繞通知,能夠完成前置通知和後置通知所實現的相同功能,可是隻須要在一個方法中實現。

例如:

在切面audience中,watchPerformance()方法提供了AOP環繞通知,包含了以前前置和後置等通知方法的全部邏輯。

public void watchPerformance(ProceedingJoinPoint joinpoint) {
    try{
        //表演以前 do something
        //...
        
        //執行被通知的方法
        joinpoint.proceed();
        
        //表演以後 do something
        //...
    }catch (Throwable t) {
        //表演失敗以後 do something
        //...
    }
}

    ProceedingJoinPoint做爲方法的入參,這個對象能讓咱們在通知裏調用被通知方法。必須調用proceed()方法,不然通知將會阻止被通知的方法。

聲明環繞通知:

<aop:config>
    <!--引用audience Bean-->
    <aop:aspect ref="audience">
    
        <!--定義切點-->
        <aop:pointcut id="performance" 
            expression="(* com.springinaction.springdol.Performer.perform(..))" />
    
        <!--聲明環繞通知-->
        <aop:around pointcut-ref="performance"
            method="watchPerformance" />
    </aop:aspect>
</aop:config
2.2.三、爲通知傳遞參數

2.2.四、經過切面注入新功能

利用<aop:declare-parents>

2.三、註解切面

    經過使用@AspectJ註解,使其不須要任何額外的類或Bean聲明就能將Audience類轉換爲一個切面。

    @Pointcut註解用於定義一個能夠在@AspectJ切面內可重用的切點,其值是一個AspectJ切點表達式。切點的名稱來源於註解所應用的方法名稱,所以,該切點的名稱爲performance(),該方法的實際內容不重要,能夠爲空的,自己只是一個標識,供@Pointcut註解依附。


    須要在Spring上下文中聲明一個自動代理Bean,該Bean知道如何把@AspectJ註解所標註的Bean轉變爲代理通知。爲此,Spring自帶了名爲AnnotationAwareAspectJAutoProxyCreator的自動代理建立類,咱們能夠在Spring上下文中把AnnotationAwareAspectJAutoProxyCreator註冊爲一個Bean,爲了簡化,Spring在aop命名空間中提供了一個自定義的配置元素:<aop:aspectj-autoproxy />。

    <aop:aspectj-autoproxy />將在Spring上下文中建立一個AnnotationAwareAspectJAutoProxyCreator類,它會自動代理一些Bean,這些Bean的方法須要與使用@AspectJ註解的Bean中所定義的切點相匹配,而這些切點又是@Pointcut註解定義出來的。

    記住,<aop:aspectj-autoproxy>僅僅使用@AspectJ註解做爲指引來建立基於代理的切面,但本質上還是一個Spring風格的切面。

2.3.一、註解環繞通知

    在這裏,@Around註解標識了watchPerformance()方法將被做爲環繞通知應用於performance()切點。

2.3.二、傳遞參數給所標註的通知

    <aop:pointcut>元素變爲@Pointcut註解,而<aop:before>元素變爲@Before註解。在這裏,惟一發生顯著變化的是@AspectJ可以依靠Java語法來判斷爲通知所傳遞參數的細節,所以這裏並不須要與<aop:before>元素的arg-names屬性所對應的註解。

2.3.三、標註引入

    等價於<aop:declare-parents>的註解是@AspectJ的@DeclareParents,在基於@AspectJ註解所標註的類內使用時,@DeclareParents工做方式幾乎等同於<aop:declare-parents>。

@DeclareParents註解由3部分組成:

  • value屬性等同於<aop:declare-parents>的type-matching屬性,標識應該被注入指定接口的Bean的類型;

  • defaultImpl屬性等同於<aop:declare-parents>的default-impl屬性,標識該類提供所引入接口的實現;

  • 由@DeclareParents註解所標註的static屬性指定了將被引入的接口。

2.四、注入AspectJ切面

    雖然Spring AOP可以知足許多切面需求,但與AspectJ相比,Spring AOP是一個功能比較弱的AOP解決方案,AspectJ提供了Spring AOP所不能支持的許多類型的切點。

相關文章
相關標籤/搜索