Spring-AOP

前言

AOP全稱是Aspect-Oriented Programming,對應到編程術語中就是面向切面的編程。簡單來講就是執行一段代碼以前以及以後分別插入另外一段代碼,從而實現對這段代碼全方位監視。java

 

術語

Jointpoint

鏈接點,抽象統一了method,constructor,field。好比最經常使用的Method,對應到類就是`ReflectiveMethodInvocation`,能夠攔截方法調用,並添加自定義的advice。這個概念是aop內部封裝的過程當中用到,使用者不須要直接接觸。spring

 

Pointcut

切入點,其實就是一個過濾器,其子類衍生出靜態的切入點,動態切入點,基於表達式的切入點等。本質就是經過對類,方法,輸入參數的解析,判斷哪些是須要進行攔截的,哪些是能夠被過濾的。編程

 

Advice

通知,或者加強,這兩種翻譯均可以強行解釋,是爲了實現攔截Jointpoint後具體的邏輯,好比常見的MethodInterceptor,對方法進行攔截。這個接口是使用者最關心的,直接關係到業務邏輯。緩存

 

核心模塊

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
    </dependency>

  

實現思路

spring支持兩種動態代理方案,1種是基於jdk的動態代理,第2種是基於cglib的動態代理。詳細參考`DefaultAopProxyFactory`,spring基於3個條件來選擇哪種動態代理方案,只要符合其中一個條件spring就會採用基於cglib的方案。1.是否須要優化(基於cglib的動態代理方案執行性能比JDK的高);2.是否聲明瞭代理對象是類(JDK不支持對類的動態代理,而cglib支持);3.沒有代理接口聲明;全部配置能夠經過`org.springframework.aop.framework.ProxyFactory`配置。用戶註冊的adivce先被緩存在列表中(Interceptor是Advice接口的子接口),若是沒有顯示聲明則會被封裝成`DefaultPointcutAdvisor`,這裏就涉及到Pointcut這個術語,默認的Pointcut容許攔截全部類和方法。ide

 

基於JDK的動態代理

核心類是`JDKDynamicAopProxy`,首先根據配置選擇型的將spring本身的3個接口加入到代理接口,而後本身實現了JDK中的`InvocationHandler`接口。當JDK的動態代理回調`InvocationHandler`的時候,先判斷是不是spring本身增長的幾個接口,若是是則經過反射調用直接返回,若是不是則經過執行`JoinPoint`的子類`ReflectiveMethodInvocation`,回調第一個advice,以後的advice經過使用者本身觸發調用,這樣造成了一個調用鏈。須要注意的是,spring爲了提升效率,額外攔截了hashcode和equals方法直接處理,也不會進入advice的攔截處理流程。假如咱們有兩個advice,那麼實際的執行順序爲性能

advice1.before -> advice2.before -> poxy.process-> advice2.after -> advice1.after

詳細demo參考優化

public class AopTest {

    public static void main(String[] args){
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        test();
    }

    public static void test(){
        // 基於對類的代理
        ProxyFactory proxyFactory = new ProxyFactory();
        // target 表示被真正執行代理的實例
        proxyFactory.setTarget(new AopTester());
        proxyFactory.setInterfaces(AopInterface.class);
        // advice 能夠對調用進行攔截從而實現aop
        proxyFactory.addAdvice(new MyInterceptor1());
        proxyFactory.addAdvice(new MyInterceptor1());
        AopInterface aopTester = (AopInterface) proxyFactory.getProxy();
        System.out.println(aopTester.run("test"));
    }

    /**
     * 被代理的接口
     */
    public static interface AopInterface{
        public String run(String s);
    }

    /**
     * 被代理的類
     */
    public static class AopTester implements AopInterface{
        @Override
        public String run(String s){
            return s+" ing ...";
        }
    }

    /**
     * 攔截器1,也是advice,提供通知
     */
    public static class MyInterceptor1 implements MethodInterceptor{
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("1 before");
            Object obj = invocation.proceed();
            System.out.println(obj);
            System.out.println("1 after");
            return "1 "+obj;
        }
    }

    /**
     * 攔截器2,也是advice,提供通知
     */
    public static class MyInterceptor2 implements MethodInterceptor{
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("2 before");
            Object obj = invocation.proceed();
            System.out.println(obj);
            System.out.println("2 after");
            return "2 "+obj;
        }
    }
}

 

總結

spring的aop以動態代理爲技術基礎,結合JDK和cglib的proxy方案,抽象出Pointcut和Advice兩個核心接口,讓使用者很是方便的控制須要攔截的內容以及攔截後的邏輯。spa

 

參考

版本 4.3.10.RELEASE翻譯

相關文章
相關標籤/搜索