Spring AOP 增強介紹

俺就不起網名發表於2018-08-22

目錄

一、前置增強

二、ProxyFactory介紹

三、後置增強和異常增強

四、環繞增強

五、引介增強


advance,通知,在Spring中叫增強也許更加合理。所謂增強,其實就是向各個程式內部注入一些程式碼從而增強原有程式的功能。
Spring使用增強類定義橫切邏輯,同時由於Spring只支援方法連線點,增強還包括在方法的哪一點加入橫切程式碼的方位資訊,所以增強即包括橫切邏輯,又包含部分連線點的資訊。

按照增強在目標類方法連線點的位置可以將增強劃分為以下五類:

增強名稱 介面 描述
前置增強

org.springframework.aop.BeforeAdvice

因為Spring只支援方法級的增強,所以MethodBeforeAdvance介面是目前可用的前置增強。

在目標方法執行前來實施增強
後置增強 org.springframework.aop.AfterReturningAdvice 在目標方法執行後來實施增強
環繞增強 org.aopalliance.intercept.MethodInterceptor 在目標方法執行前、後實施增強。環繞增強是AOP聯盟定義的介面,其他四種增強介面則是Spring定義的介面。
異常丟擲增強 org.springframework.aop.ThrowsAdvice 在目標方法丟擲異常後來實施增強
引介增強 org.springframework.aop.Introductioninterceptor 表示在目標類中新增一些新的方法和屬性。引介增強是一種特殊的增強。他可以在目標類中新增屬性和方法,通過攔截定義一個介面,讓目標代理實現這個介面。他的連線點是類級別的,而前面的幾種則是方法級別的。

通過實現這些增強介面,在實現這些介面的方法當中定義橫切邏輯,就可以將它們織入目標類方法的相應連線點位置。

一、前置增強

增強目標類

public class NativeWaiter {
    public void greetTo(String name) {
        System.out.println("greet to " + name + "...");
    }
    public void serveTo(String name) {
        System.out.println("serving to " + name + "...");
    }
}

增強類,實現 MethodBeforeAdvice 介面

public class GreetingBeforeAdvice implements MethodBeforeAdvice {
    /**
     * 前置增強方法
     * 當該方法發生異常時,將阻止目標方法的執行
     *
     * @param method  目標類方法
     * @param objects 目標類方法入參
     * @param o       目標類物件例項
     */
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        String name = (String) objects[0];
        System.out.println("How are you!Mr." + name + ".");
    }
}

測試類,使用cglib生成代理物件

    @Test
    public void before_cglib_proxy() {
        //建立目標物件
        NativeWaiter target = new NativeWaiter();
        //建立前置增強器
        BeforeAdvice advice = new GreetingBeforeAdvice();
        //建立代理工廠物件
        ProxyFactory pf = new ProxyFactory();
        //設定代理類
        pf.setTarget(target);
        //也可以啟動優化代理方式,會使用cglib來生成代理。當使用介面時,只要呼叫了該方法,都會使用cglib方式來生成代理物件
        //pf.setOptimize(true);
        //設定增強類
        pf.addAdvice(advice);
        //生成代理例項
        NativeWaiter proxy = (NativeWaiter) pf.getProxy();
        proxy.greetTo("John");
        proxy.serveTo("Tom");
    }

執行結果

How are you!Mr.John.
greet to John...
How are you!Mr.Tom.
serving to Tom...

總結:

1、增強器類,實現 MethodBeforeAdvice 介面;

2、利用Spring的ProxyFactory,加入代理目標和增強類,然後生成代理物件;

二、ProxyFactory介紹

1、基本使用

其實ProxyFactory代理技術就是利用jdk代理或者cglib代理的技術,將增強應用到目標類當中。

Spring定義的AopProxy介面具有兩個final型別的實現類:

CglibAopProxy : 是使用cglib代理技術來建立代理
JdkDynamicAopProxy : 是使用jdk代理技術來建立代理

使用proxyFactory.setInterfaces(target.getClass().getInterfaces())的方法指定目標介面進行代理,則ProxyFactory使用JdkDynamicAopProxy;如果針對類的代理,則使用CglibAopProxy,此外還可以通過ProxyFactory的setOptimize(true)方法啟動優化代理方式,這樣針對介面的代理也會使用CglibAopProxy。比如上面的前置增強BeforeAdviceTest就是使用CglibAopProxy來生成動態代理類的。

那麼使用JDK代理如何來實現的呢?程式碼如下:

首先,定義介面Waiter,並讓NaiveWaiter實現該介面

public interface Waiter {
    void greetTo(String name);
    void serveTo(String name);
}
public class NaiveWaiter implements Waiter {
	//該類具體實現程式碼不變
}

然後,使用jdk代理具體實現方法

    @Test
    public void before_jdk_proxy() {
        //建立目標物件
        Waiter target = new NaiveWaiter();
        //建立前置增強器
        BeforeAdvice advice = new GreetingBeforeAdvice();
        //建立代理工廠物件
        ProxyFactory pf = new ProxyFactory();
        //設定代理介面
        pf.setInterfaces(target.getClass().getInterfaces());
        //設定代理類
        pf.setTarget(target);
        //設定增強類
        pf.addAdvice(advice);
        //生成代理例項
        Waiter proxy = (Waiter) pf.getProxy();
        proxy.greetTo("John");
        proxy.serveTo("Tom");
    }

如上ProxyFactory.setInterfaces(target.getClass().getInterfaces())方法,該方法會獲取到介面並將其載入進去。當目標物件如果沒有實現任何介面時,那麼即使使用了該方法,生成代理物件時也是使用CglibAopProxy來生成的。如果目標物件實現了一個介面,那麼ProxyFactory.getProxy()方法,返回物件必須轉換成介面型別。

ProxyFactory通過addAdvice來增加一個增強。使用者可以使用該方法增加多個增強,通過增強形成一個增強鏈,他們的呼叫順序和新增順序是一致的

2、Spring通過配置檔案實現增強

在bean.xml配置檔案中進行如下配置

    <bean id="gerrtingBefore" class=" demo04.advance.GreetingBeforeAdvice"/>
    <bean id="target" class="demo04.advance.NativeWaiter"/>
    <bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyInterfaces="demo04.advance.Waiter"
          p:interceptorNames="gerrtingBefore"
          p:target-ref="target"
    />

根據以上配置資訊,介紹下ProxyFactoryBean配置檔案當中常用的屬性:

target:我們需要代理的目標物件
proxyInterfaces:代理所要實現的介面,可以是多個介面
interceptorNames:需要織入的目標物件的Bean的列表(增強類的Bean列表),使用Bean的名稱來指定。
singleton:確定返回的代理是不是單例項的,系統預設返回的是單例項的。
optimize:當值為true時,強制使用cglib代理。當是singleton的例項時我們推薦使用cglib代理,當是其他作用域的時候,推薦使用JDK的代理。原因是cglib建立代理速度比較慢,但是執行效率高。JDK代理則剛好相反。
proxyTargetClass:是否對類進行代理而不是對介面進行代理,當值為true的時候使用cglib代理。

測試程式碼:

    @Test
    public void before_advice_xml_test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans/beans.xml");
        Waiter waiter = context.getBean("waiter", Waiter.class);
        waiter.greetTo("JayChou");
    }

三、後置增強和異常增強

在上面的例子中,前置增強實現了MethodBeforeAdvice介面,後置增強、異常增強和前置增強一樣,只需實現相應的介面就可以了。

四、環繞增強

實現介面MethodInterceptor,其他與前置增強一樣

public class GreetingInterceptor implements MethodInterceptor{
    /**
    * 業務邏輯實現類
    * @param methodInvocation 封裝了目標方法和入引數組以及目標方法所帶的例項物件
    * @return 代理物件
    * @throws Throwable
    */
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        //獲取目標方法的入參
        Object[] args=methodInvocation.getArguments();
        //獲取方法名稱
        String clickName= (String) args[0];
        System.out.println("GreetingInterceptor:How are you!");
        //利用反射機制來呼叫目標方法
        Object object=methodInvocation.proceed();
        System.out.println("GreetingInterceptor: please enjoy youself!");
        return object;
    }
}

五、引介增強

引介增強的連線點是類級別的而非方法級別的,通過引介增強我們可以為目標類新增一個介面的實現即原來目標類未實現某個介面,那麼通過引介增強可以為目標類建立實現某介面的代理。Spring為引介增強提供了Introductioninterceptor介面,該介面未提供任何方法,所以一般在它的實現類DelegatingIntroductionInterceptor中進行擴充套件。


總結:

1、按照增強在目標類方法連線點的位置可以將增強劃分為五類:前置增強、後置增強、環繞增強、異常增強、引介增強;

2、Spring定義了內部代理工廠類ProxyFactory,它利用jdk代理或者cglib代理的技術,將增強應用到目標類當中。

3、Spring定義的AopProxy介面具有兩個final型別的實現類:

CglibAopProxy : 是使用cglib代理技術來建立代理

JdkDynamicAopProxy : 是使用jdk代理技術來建立代理

4、實現一個目標類的增強,首先實現增強相關介面,然後將目標類和增強物件放入代理工廠中,最後代理工廠生成代理物件;

 

 

 

 

相關文章