spring原始碼解讀-aop

flynike發表於2021-09-09

  aop是指面向切面程式設計,ProxyFactoryBean是spring aop的底層實現與源頭,為什麼這麼說呢?首先我們看一段配置:

圖片描述

  1、target是目標物件,需要對其進行切面增強

  2、proxyInterfaces是指代理物件所實現的介面

  3、interceptorNames:是指通知器(Advisor)列表,通知器中包含了通知advice與切點pointcut

 

  概括一下,ProxyFactoryBean的作用是:

  針對目標物件來建立代理物件,將對目標物件方法的呼叫轉到對相應代理物件方法的呼叫,並且可以在代理物件方法呼叫前後執行與之匹配的各個通知器中定義好的方法

 

  spring透過兩種方式來建立目標代理物件:

  1、JDK動態代理

  2、CGLIB

 

  在spring中這兩種代理方式都實現了AopProxy介面去,如下圖所示:

圖片描述

 

  spring中AopProxyFactory是一個介面,介面中只有一個方法createAopProxy,這個介面是有一個實現類DefaultAopProxyFactory,用來建立AopProxy物件,如下圖所示:

圖片描述

 

  下面我們比較並實現以下兩種代理方式:

   首先明確以下我們常用的代理模式有什麼問題,在我們經常使用的代理模式中,真實物件必須是事先存在的,並將其作為代理物件的內部屬性,在使用時,一個真實角色必須對應一個dialing角色,如果大量使用會導致類的急劇膨脹,java的動態代理類就可以解決這個問題。

  JDK動態代理

  java動態代理類位於java.lang.reflect包下,一般主要涉及到兩個類:

  1、Interface InvocationHandler:該介面中只定義了一個方法

 public Object invoke(Object proxy, Method method, Object[] args)        throws Throwable;

  第一個引數proxy是指代理類,method是被代理的方法,args為該方法的引數陣列

 

  2、Proxy:該類為動態代理類,其主要方法如下

public static Object newProxyInstance(ClassLoader loader,
                                          Class>[] interfaces,
                                          InvocationHandler h)        throws IllegalArgumentException

  該方法返回代理類的一個例項,返回後的代理類可以被當作代理類使用

 

  JDK動態代理實現步驟:

  1、建立一個實現介面InvocationHandler的類(DynamicSubject),它必須實現invoke方法

圖片描述

public class DynamicSubject implements InvocationHandler {    private Object sub;    public DynamicSubject(Object sub) {        this.sub = sub;
    }    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("before calling:" + method);
        method.invoke(sub, args);
        System.out.println(args == null);
        System.out.println("after calling:" + method);        return null;
    }
}

圖片描述

 

  2、建立被代理的類(RealSubject)以及介面(Subject)

public interface Subject {    public void request();
}
public class RealSubject implements Subject {    public void request() {
        System.out.println("from real subject");
    }
}

 

  3、透過Proxy的靜態方法newProxyInstance建立一個代理

圖片描述

public class Client {    public static void main(String[] args) {

        RealSubject realSubject = new RealSubject();
        InvocationHandler handler = new DynamicSubject(realSubject);
        Class> classType = handler.getClass();
        Subject subject = (Subject) Proxy.newProxyInstance(classType.getClassLoader(), realSubject.getClass().getInterfaces(), handler);
        subject.request();
        System.out.println(subject.getClass());
    }
}

圖片描述

 

  如果目標類並未實現介面,那麼Spring就會使用CGLIB庫為其建立代理,如下demo運用了CGLIB實現代理

  1、建立被代理類Person

public class Person {    public void study() {
        System.out.println("study");
    }
}

 

  2、建立CglibProxy實現代理

圖片描述

public class CglibProxy implements MethodInterceptor {    private Enhancer enhancer = new Enhancer();    public Object getProxy(Class> clazz) {
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        Object object = enhancer.create();        return object;
    }    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {

        System.out.println(o.getClass().getName() + "." + method.getName());        //執行父類對應方法
        final Object result = methodProxy.invokeSuper(o, objects);

        System.out.println("執行結束");        return result;
    }    public static void main(String[] args) {

        CglibProxy proxy = new CglibProxy();
        Person person = (Person) proxy.getProxy(Person.class);
        System.out.println(person.getClass().getName());
        person.study();

    }
}

圖片描述

 

  下面我們來比較一下,兩種代理方法的區別

  1、JDK動態代理只能對實現了介面的類生成代理,而不能針對類

  2、CGLIB是針對類實現代理,主要是對指定的類生成一個子類,覆蓋其中的方法

  3、在spring中如果bean實現了介面spring用JDK動態代理,如果bean沒有實現介面,spring使用CGLIB實現代理

  4、CGLIb不能對宣告為final的方法進行dialing,因為CGLib原理是動態生成被代理類的子類

  5、JDK動態代理透過反射的newInstance方法產生代理類的物件

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2618/viewspace-2803760/,如需轉載,請註明出處,否則將追究法律責任。

相關文章