28--Aop知識點回顧以及基于Advice接口的增強實現(xiàn)

上一章節(jié)分析了靜態(tài)代理和JDK、CGLIB動態(tài)代理,接下來我們還要回顧一下AOP的一些相關(guān)知識,以方便為接下來分析AOP的源碼做好準(zhǔn)備。

1.先來回顧一下AOP中的一些術(shù)語。
  • 連接點(Jointpoint):表示需要在程序中插入橫切關(guān)注點的擴展點,連接點可能是類初始化、方法執(zhí)行、方法調(diào)用、字段調(diào)用或處理異常等等,Spring只支持方法執(zhí)行連接點,在AOP中表示為“在哪里干”;
  • 切入點(Pointcut):選擇一組相關(guān)連接點的模式,即可以認(rèn)為連接點的集合,Spring支持perl5正則表達式和AspectJ切入點模式,Spring默認(rèn)使用AspectJ語法,在AOP中表示為“在哪里干的集合”;
  • 增強(Advice):很多地方理解為通知,但是理解為增強更為準(zhǔn)確,增強表示在連接點上執(zhí)行的行為,增強提供了在AOP中需要在切入點所選擇的連接點處進行擴展現(xiàn)有行為的手段;包括前置增強(before advice)、后置增強(after advice)、環(huán)繞增強(around advice),在Spring中通過代理模式實現(xiàn)AOP,并通過攔截器模式以環(huán)繞連接點的攔截器鏈織入增強;在AOP中表示為“干什么”;
  • 方面/切面(Aspect):橫切關(guān)注點的模塊化,比如上邊提到的日志組件??梢哉J(rèn)為是增強、引入和切入點的組合;在Spring中可以使用Schema和@AspectJ方式進行組織實現(xiàn);在AOP中表示為“在哪干和干什么集合”;
  • 引介增強(inter-type declaration):引介增強是一個比較特殊的增強,它不是在目標(biāo)方法周圍織入增強,而是為目標(biāo)類創(chuàng)建新的方法或?qū)傩裕砸樵鰪姷倪B接點是類級別的,而非方法級別的,Spring允許引入新的接口(必須對應(yīng)一個實現(xiàn))到所有被代理對象(目標(biāo)對象), 在AOP中表示為“干什么(引入什么)”;
  • 目標(biāo)對象(Target Object):需要被織入橫切關(guān)注點的對象,即該對象是切入點選擇的對象,需要被增強的對象,從而也可稱為“被增強對象”;由于Spring AOP 通過代理模式實現(xiàn),從而這個對象永遠(yuǎn)是被代理對象,在AOP中表示為“對誰干”;
  • AOP代理(AOP Proxy):AOP框架使用代理模式創(chuàng)建的對象,從而實現(xiàn)在連接點處插入增強(即應(yīng)用切面),就是通過代理來對目標(biāo)對象應(yīng)用切面。在Spring中,AOP代理可以用JDK動態(tài)代理或CGLIB代理實現(xiàn),而通過攔截器模型應(yīng)用切面。
  • 織入(Weaving):織入是一個過程,是將切面應(yīng)用到目標(biāo)對象從而創(chuàng)建出AOP代理對象的過程,織入可以在編譯期、類裝載期、運行期進行。

上面所提到的概念,比較抽象,也比較枯燥,而且在實際的開發(fā)中使用切面編程只占很少一部分,但是如果大家對上面的概念無所了解的話,那么對接下來的源碼分析必然是一頭霧水,下面我們再通過幾個例子,讓大家對上面的概念有所了解,為源碼分析做好準(zhǔn)備。

2. 增強方式簡介

也有很多人將增強理解為通知,但是理解為增強會更加準(zhǔn)確,因為它表示在連接點上執(zhí)行的行為,這個行為是目標(biāo)類類所沒有的,是為目標(biāo)類增加了額外的方法或者其他的一些功能,所以理解為增強比通知更加貼切,當(dāng)然如果有的人已經(jīng)習(xí)慣了通知這個概念的話也無所謂,只要知道本文將的增強即是通知即可。

增強的類型有前置增強、后置返回增強、異常增強、環(huán)繞增強、引介增強、后置最終增強等。下面我們通過實例的例子來介紹一下,為了讓大家能夠更為深刻的理解SpringAop,我們不會一開始就講解基于@AspectJ或者基于Schema配置文件的方式,而是從最基礎(chǔ)開始講解,畢竟AOP在實際開發(fā)中并不占太大的比重,相信很多人并沒有深刻的理解。

接下來我們先講解基于Advice接口以編碼方式實現(xiàn)的增強。

3.MethodBeforeAdvice前置增強
  • 目標(biāo)接口和實現(xiàn)類
package com.lyc.cn.v2.day04.advisor;

/**
 * @author: LiYanChao
 * @create: 2018-11-01 21:48
 */
public interface Animal {
    void sayHello(String name,int age);
    void sayException(String name,int age);
}
package com.lyc.cn.v2.day04.advisor;

/**
 * @author: LiYanChao
 * @create: 2018-11-01 21:48
 */
public class Dog implements Animal {

    @Override
    public void sayHello(String name, int age) {
        System.out.println("==名字:" + name + " 年齡:" + age);
    }

    @Override
    public void sayException(String name, int age) {
        System.out.println("==名字:" + name + " 年齡:" + age);
        System.out.println("==拋出異常:" + 1 / 0);
    }
}
  • 前置增強
package com.lyc.cn.v2.day04.advisor;
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;

/**
 * 前置增強
 * @author: LiYanChao
 * @create: 2018-11-01 21:50
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    /**
     * Callback before a given method is invoked.
     * @param method method being invoked
     * @param args   arguments to the method
     * @param target target of the method invocation. May be {@code null}.
     * @throws Throwable if this object wishes to abort the call.
     *                   Any exception thrown will be returned to the caller if it's
     *                   allowed by the method signature. Otherwise the exception
     *                   will be wrapped as a runtime exception.
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("==前置增強");
        System.out.println("==方法名:" + method.getName());
        if (null != args && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                System.out.println("==第" + (i + 1) + "參數(shù):" + args[i]);
            }
        }
        System.out.println("==目標(biāo)類信息:" + target.toString());
    }
}
4.AfterReturningAdvice后置增強
package com.lyc.cn.v2.day04.advisor;
import org.springframework.aop.AfterReturningAdvice;
import java.lang.reflect.Method;

/**
 * 后置增強
 * @author: LiYanChao
 * @create: 2018-11-01 22:09
 */
public class MyAfterReturningAdvice implements AfterReturningAdvice {
    /**
     * Callback after a given method successfully returned.
     * @param returnValue the value returned by the method, if any
     * @param method      method being invoked
     * @param args        arguments to the method
     * @param target      target of the method invocation. May be {@code null}.
     * @throws Throwable if this object wishes to abort the call.
     *                   Any exception thrown will be returned to the caller if it's
     *                   allowed by the method signature. Otherwise the exception
     *                   will be wrapped as a runtime exception.
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("==后置增強");
        System.out.println("==方法名:" + method.getName());
        if (null != args && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                System.out.println("==第" + (i + 1) + "參數(shù):" + args[i]);
            }
        }
        System.out.println("==目標(biāo)類信息:" + target.toString());
    }
}
5.ThrowsAdvice異常增強
package com.lyc.cn.v2.day04.advisor;
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;

/**
 * @author: LiYanChao
 * @create: 2018-11-01 22:17
 */
public class MyThrowsAdvice implements ThrowsAdvice {

    /**
     * 異常增強
     */
    public void afterThrowing(Method method, Object[] args, Object target, Exception ex) {
        System.out.println("==異常增強");
        System.out.println("==方法名:" + method.getName());
        if (null != args && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                System.out.println("==第" + (i + 1) + "參數(shù):" + args[i]);
            }
        }
        System.out.println("==目標(biāo)類信息:" + target.toString());
        System.out.println("==異常信息:" + ex.toString());
    }
}
6.MethodInterceptor環(huán)繞增強
package com.lyc.cn.v2.day04.advisor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * 環(huán)繞增強
 * @author: LiYanChao
 * @create: 2018-11-01 22:29
 */
public class MyMethodInterceptor implements MethodInterceptor {

    /**
     * 環(huán)繞增強 這里的方法參數(shù)與之前的前置增強、后置增強明顯不同,只有一個MethodInvocation類型的參數(shù)
     * Implement this method to perform extra treatments before and
     * after the invocation. Polite implementations would certainly
     * like to invoke {@link Joinpoint#proceed()}.
     * @param invocation the method invocation joinpoint
     * @return the result of the call to {@link Joinpoint#proceed()};
     * might be intercepted by the interceptor
     * @throws Throwable if the interceptors or the target object
     *                   throws an exception
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("==環(huán)繞增強開始");
        System.out.println("==方法名:" + invocation.getMethod().getName());
        Object[] args = invocation.getArguments();
        if (null != args && args.length > 0) {
            for (int i = 0; i < args.length; i++) {
                System.out.println("==第" + (i + 1) + "參數(shù):" + args[i]);
            }
        }

        Object proceed = invocation.proceed();

        System.out.println("==環(huán)繞增強結(jié)束");
        return proceed;
    }
}
7.測試及結(jié)果
@Test
public void test5() {
    // 前置增強
    // 1、實例化bean和增強
    Animal dog = new Dog();
    MyMethodBeforeAdvice advice = new MyMethodBeforeAdvice();

    // 2、創(chuàng)建ProxyFactory并設(shè)置代理目標(biāo)和增強
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTarget(dog);
    proxyFactory.addAdvice(advice);

    // 3、生成代理實例
    Animal proxyDog = (Animal) proxyFactory.getProxy();
    proxyDog.sayHello("二哈", 3);
}


@Test
public void test6() {
    // 后置增強
    // 1、實例化bean和增強
    Animal dog = new Dog();
    MyAfterReturningAdvice advice = new MyAfterReturningAdvice();

    // 2、創(chuàng)建ProxyFactory并設(shè)置代理目標(biāo)和增強
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTarget(dog);
    proxyFactory.addAdvice(advice);

    // 3、生成代理實例
    Animal proxyDog = (Animal) proxyFactory.getProxy();
    proxyDog.sayHello("二哈", 3);

}

@Test
public void test7() {
    // 異常增強
    // 1、實例化bean和增強
    Animal dog = new Dog();
    MyThrowsAdvice advice = new MyThrowsAdvice();

    // 2、創(chuàng)建ProxyFactory并設(shè)置代理目標(biāo)和增強
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTarget(dog);
    proxyFactory.addAdvice(advice);

    // 3、生成代理實例
    Animal proxyDog = (Animal) proxyFactory.getProxy();
    proxyDog.sayException("二哈", 3);

}


@Test
public void test8() {
    // 環(huán)繞增強
    // 1、實例化bean和增強
    Animal dog = new Dog();
    MyMethodInterceptor advice = new MyMethodInterceptor();

    // 2、創(chuàng)建ProxyFactory并設(shè)置代理目標(biāo)和增強
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.setTarget(dog);
    proxyFactory.addAdvice(advice);

    // 3、生成代理實例
    Animal proxyDog = (Animal) proxyFactory.getProxy();
    proxyDog.sayHello("二哈", 3);

}
信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@2280cdac]
==前置增強
==方法名:sayHello
==第1參數(shù):二哈
==第2參數(shù):3
==目標(biāo)類信息:com.lyc.cn.v2.day04.advisor.Dog@2280cdac
==名字:二哈 年齡:3
十一月 03, 2018 10:32:46 下午 org.springframework.aop.framework.CglibAopProxy getProxy
信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@6b2fad11]
==名字:二哈 年齡:3
==后置增強
==方法名:sayHello
==第1參數(shù):二哈
==第2參數(shù):3
==目標(biāo)類信息:com.lyc.cn.v2.day04.advisor.Dog@6b2fad11
十一月 03, 2018 10:32:46 下午 org.springframework.aop.framework.CglibAopProxy getProxy
信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@38082d64]
==名字:二哈 年齡:3
==異常增強
==方法名:sayException
==第1參數(shù):二哈
==第2參數(shù):3
==目標(biāo)類信息:com.lyc.cn.v2.day04.advisor.Dog@38082d64
==異常信息:java.lang.ArithmeticException: / by zero

十一月 03, 2018 10:32:46 下午 org.springframework.aop.framework.CglibAopProxy getProxy
信息: Creating CGLIB proxy: target source is SingletonTargetSource for target object [com.lyc.cn.v2.day04.advisor.Dog@3f2a3a5]

java.lang.ArithmeticException: / by zero

    at com.lyc.cn.v2.day04.advisor.Dog.sayException(Dog.java:17)
    at com.lyc.cn.v2.day04.advisor.Dog$$FastClassBySpringCGLIB$$a974b1ec.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
==環(huán)繞增強開始
==方法名:sayHello
==第1參數(shù):二哈
==第2參數(shù):3
==名字:二哈 年齡:3
==環(huán)繞增強結(jié)束
8.總結(jié)

以上簡單介紹了前置增強、后置增強、環(huán)繞增強、異常增強等以編碼實現(xiàn)的方式,當(dāng)然以上實現(xiàn)也可以通過配置文件的方式實現(xiàn)。本篇所介紹的知識點比較簡單,但是理解增強的概念是AOP的基礎(chǔ),其實本篇的各種增強方式用上一篇講解的動態(tài)代理是完全可以實現(xiàn)的,因為本篇使用的代理工廠ProxyFactory內(nèi)部使用的是CglibAopProxyJdkDynamicAopProxy,其實其本質(zhì)還是JDK或CGLIB動態(tài)代理,在接下來的章節(jié)中會詳細(xì)講解CglibAopProxyJdkDynamicAopProxy的實現(xiàn),本篇的分析就到這里了。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文是我自己在秋招復(fù)習(xí)時的讀書筆記,整理的知識點,也是為了防止忘記,尊重勞動成果,轉(zhuǎn)載注明出處哦!如果你也喜歡,那...
    波波波先森閱讀 12,444評論 6 86
  • AOP實現(xiàn)可分為兩類(按AOP框架修改源代碼的時機): 靜態(tài)AOP實現(xiàn):AOP框架在編譯階段對程序進行修改,即實現(xiàn)...
    數(shù)獨題閱讀 2,401評論 0 22
  • A Roc's flight of ten thousand li-A bright future In the ...
    蘇偉權(quán)閱讀 668評論 0 0
  • 文/烏江子 2017年5月11日,福建泉州豐澤一名初二學(xué)生因為學(xué)習(xí)時間玩手機,被老師家長批評。凌晨,男孩用小刀割手...
    奔泉閱讀 2,187評論 0 2
  • 正在和媽媽打電話,然后說起家里面的事情,在火車南站買的房子已經(jīng)把所有的錢都結(jié)完賬了,但是村兒里的房子又要動工開修,...
    萌萌噠寶批龍居閱讀 226評論 0 0

友情鏈接更多精彩內(nèi)容