靜態代理與JDK動態代理與CGLIB動態代理

爲了理解spring的AOP。 理解JDK動態代理與CGLB動態代理,很是重要。html

講動態代理必講靜態代理。java

靜態代理與動態代理是一種設計思想git

靜態代理

靜態代理是在編譯期將擴展代碼織入代理對象github

實現方式spring

1.代理模式:能夠理解爲硬編碼模式。就是代理類裏持有被代理對象的引用。 經過調用代理類的實例。達到間接調用被代理對象的目的。ide

public interface UserInterface {
    void doSomething();
}

public class User implements UserInterface {
    @Override
    public void doSomething() {
        System.out.println("I'm doing something");
    }
}
public class UserProxy implements UserInterface {
    private UserInterface userInterface = null; // 持有被代理對象的引用
    @Override
    public void doSomething() {
        beforeDoSomething();
        if(operator == null){
            userInterface =  new User();
        }
        operator.doSomething();
        afterDoSomething();
    }
    private void beforeDoSomething() {
        System.out.println("before doing something");
    }
    private void afterDoSomething() {
        System.out.println("after doing something");
    }
}
複製代碼

2.AspectJ編譯織入 AspectJ是一種面前切面的技術,經過在編譯期把AspectJ內容編譯到字節碼文件.class (這裏不作過多解析推薦文章關於 Spring AOP (AspectJ) 你該知曉的一切this

靜態代理小結:編碼

  • 靜態代理不產生新的Class
  • 靜態代理在編譯期織入代理對象
  • 靜態代理不靈活。

動態代理:

咱們熟知的動態代理:JDK動態代理和CGLB動態代理 他們都有一個共同點, 在內存中生成了新的字節碼,這點很重要。這節咱們從生成的新字節碼的角度來看看動態代理的兩種方式spa

由於新生成的字節碼在內存中,爲了看到這些字節碼咱們須要其以文件的形式展示出來。咱們在main方法里加下面兩行代碼就能夠把內存的字節碼拿出來了.net

//輸出JDK動態代理
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
//輸出CGLIB動態代理產生的類
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./cglbproxy");
複製代碼

咱們仍是以User類爲例看看JDK動態代理生成的字節碼文件與CGLB生成的字碼文件 詳情代碼參考springlean項目

1.JDK動態代理:

//------------------------------------接口
interface UserInterface {
    void doSomething();
}
//------------------------------------被代理類
class User implements UserInterface {
    @Override
    public void doSomething() {
        System.out.println("我是實現了接口的User");
    }
}
//------------------------------------代理類(也是攔截器)
class JDKProxy implements InvocationHandler {

    // 要被代理的目標對象
    private UserInterface target;

    public JDKProxy(UserInterface target){
        this.target=target;
    }
    public UserInterface createProxy(){
        return (UserInterface) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("JDK前置攔截");
        return method.invoke(target,args);
    }
}
//------------------------------------內存生成的新代理類。
final class $Proxy0 extends Proxy implements UserInterface {
    private static Method m3;
  	//省略其餘代碼
  	.....
    public final void doSomething() throws {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    static {
        try {
            m3 = Class.forName("cn.wqd.UserInterface").getMethod("doSomething", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
    ....
}
//------------------------------------調用。
public static void main(String[] args) {
 		User a=new User();
        //建立JDK代理
        JDKProxy jdkProxy=new JDKProxy(a);
        //建立代理對象
        UserInterface proxy=jdkProxy.createProxy();
        System.out.println(proxy.getClass().getName());
        //執行代理對象方法
        proxy.doSomething();
}
複製代碼

System.out.println(proxy.getClass().getName()); 輸出的是cn.wqd.$Proxy0。 咱們沒有定義過cn.wqd.$Proxy0類,因此 cn.wqd.$Proxy0就是新生成的代理類。

對比咱們User類與$Proxy0類總結下新的代理類的特徵

  • 新生成的代理類中定義了與被代理對象相同方法名的方法,如doSomething()方法
  • 新生成的代理類實現了與被代理類相同接口 :UserInterface
  • 新生成的代理類繼承了Proxy
  • 新生成的代理類是final類型

當咱們調用proxy.doSomething()的時候,指的是cn.wqd.$Proxy0.doSomething() ,其內部調用時父類Proxy的.InvocationHandler的invoke()方法,傳入的參數(1)代理類對象 ,(2)目標對象的方法。

InvocationHandler屬性參數其實就是咱們定義的JDKProxy,其invoke方法最終調用目標對象方法 method.invoke(target,args)。以此達到代理的目的。

小結一下:

  • JDK動態代理經過反射達到代理的目的
  • JDK動態代理會在內存生成一個繼承了Proxy,實現了同被代理類實現的接口相同的接口的 代理類。
  • JDK動態代理執行鏈:代理類方法-->InvocationHandler.invoke()-->目標方法

2.CGLB動態代理:

//====================user
class UserNoInterface{

    public void doSomething() {
        System.out.println("我是沒有實現接口的User");
    }
}
//====================攔截器
class CglibProxyIntercepter implements MethodInterceptor {
    @Override
    public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("執行前...");
        Object object = methodProxy.invokeSuper(sub, objects);
        System.out.println("執行後...");
        return object;
    }
}

//------------------------------------調用。
public static void main(String[] args) {
 		Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserNoInterface.class);
        enhancer.setCallback(new CglibProxyIntercepter());
        UserNoInterface proxyCGLB= (UserNoInterface)  enhancer.create();
        System.out.println(proxyCGLB.getClass().getName());
        proxyCGLB.doSomething();
}
複製代碼

CGLB動態代理不要求被代理類實現一個接口,就能夠進行代理。 CGLB生成三個新的字碼碼文件,是否是很詭異。

file

System.out.println(proxyCGLB.getClass().getName()); 打印cn.wqd.UserNoInterface$$EnhancerByCGLIB$$b3361405 看來三個文件中。 cn.wqd.UserNoInterface$$EnhancerByCGLIB$$b3361405纔是對應生成的代理類。 咱們分析下代理類代碼。(爲了便於理解,我只貼出有助於理解的代碼具體查看詳情代碼參考springlean項目

public class UserNoInterface$$EnhancerByCGLIB$$b3361405 extends UserNoInterface implements Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$doSomething$0$Method;
    private static final MethodProxy CGLIB$doSomething$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("cn.wqd.UserNoInterface$$EnhancerByCGLIB$$b3361405");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$1$Method = var10000[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = var10000[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = var10000[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
        CGLIB$doSomething$0$Method = ReflectUtils.findMethods(new String[]{"doSomething", "()V"}, (var1 = Class.forName("cn.wqd.UserNoInterface")).getDeclaredMethods())[0];
        CGLIB$doSomething$0$Proxy = MethodProxy.create(var1, var0, "()V", "doSomething", "CGLIB$doSomething$0");
    }

    final void CGLIB$doSomething$0() {
        super.doSomething();
    }

    public final void doSomething() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if(this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 != null) {
            var10000.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy);
        } else {
            super.doSomething();
        }
    }
	.....
}
複製代碼

總結下特徵

  • 代理類繼承了被代理類,
  • 代理類爲每一個目標類的方法都生成了兩個方法,以doSomething()爲例,一個是重寫方法doSomething(),另外一個是CGLIB$doSomething$0()CGLIB$doSomething$0()方法內部調用的是super.doSomething()也就是目標方法的doSomething()方法
  • 代理類會得到全部在父類繼承來的方法,而且會建立一個MethodProxy類型屬性與之對應。以doSomething()例子,CGLIB$doSomething$0$Method獲取的父類方法,CGLIB$doSomething$0$Proxy與之對應的MethodProxy
CGLIB$doSomething$0$Method = ReflectUtils.findMethods(new String[]{"doSomething", "()V"}, (var1 = Class.forName("cn.wqd.UserNoInterface")).getDeclaredMethods())[0];
CGLIB$doSomething$0$Proxy = MethodProxy.create(var1, var0, "()V", "doSomething", "CGLIB$doSomething$0");
複製代碼

由於拿到的proxyCGLB是代理類對象實例,因此proxyCGLB.doSomething()調用的就是

public final void doSomething() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if(this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 != null) {
            var10000.intercept(this, CGLIB$doSomething$0$Method, CGLIB$emptyArgs, CGLIB$doSomething$0$Proxy);
        } else {
            super.doSomething();
        }
    }
複製代碼

咱們看看這段邏輯

首先,檢查(MethodInterceptor)CGLIB$CALLBACK_0是否爲null,看到MethodInterceptor咱們應該明白了這個地方的CGLIB$CALLBACK_0應該就是上文定義的CglibProxyIntercepter 。

不爲null。則執行CglibProxyIntercepter.intercept()方法,入參分別爲:

  • this:代理類對象,
  • CGLIB$doSomething$0$Method目標對象方法
  • CGLIB$emptyArgs方法入參
  • CGLIB$doSomething$0$Proxy代理方法。

CglibProxyIntercepter.intercept()方法內部經過調用MethodProxy.invokeSuper(sub, objects)。

咱們看看MethodProxy的內部

public Object invoke(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f1.invoke(fci.i1, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        } catch (IllegalArgumentException var5) {
            if(this.fastClassInfo.i1 < 0) {
                throw new IllegalArgumentException("Protected method: " + this.sig1);
            } else {
                throw var5;
            }
        }
    }
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            this.init();
            MethodProxy.FastClassInfo fci = this.fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException var4) {
            throw var4.getTargetException();
        }
    }
    ///fastclass
 	private static class FastClassInfo {
        FastClass f1;
        FastClass f2;
        int i1;
        int i2;

        private FastClassInfo() {
        }
    }
複製代碼

這裏有invoke()與invokeSuper()。爲啥上面要調用invokeSuper()不調用invoke()呢?


這裏就要涉及到CGLB的FastClass機制。 FastClass.機制:爲代理類和被代理類各生成一個Class。這個Class會爲代理類或被代理類的方法分配一個index(int類型)索引。經過這個索引能夠直接定位到要調用的方法,省去了反射。 具體推薦閱讀 參考1 參考2

這裏咱們只需知道結果:MethodProxy.FastClassInfo 屬性的最終值會是

private static class FastClassInfo {
        FastClass f1;//被代理類UserNoInterface的FastClass
        FastClass f2;//代理類UserNoInterface$$EnhancerByCGLIB$$b3361405的FastClass
        int i1;//被代理類的doSomething()的索引
        int i2;//代理類CGLIB$doSomething$0()(內部調用被代理對象的方法)的索引。
        private FastClassInfo() {
        }
    }
複製代碼

三個文件分別爲:

  • UserNoInterface$$EnhancerByCGLIB$$b3361405$$FastClassByCGLIB$$fc90c93c《代理類的FastClass》
  • UserNoInterface$$EnhancerByCGLIB$$b3361405《 cglb生成的代理類》
  • UserNoInterface$$FastClassByCGLIB$$29e52466《被代理類的FastClass》

咱們在回頭看看invoke(),invokeSuper()方法

  • invoke() 調用的是fci.f1.invoke(fci.i1, obj, args);也就是調用的是被代理類UserNoInterface的FastClass的invoke方法。
  • invokeSuper()調用的是fci.f2.invoke(fci.i2, obj, args);也就是調用的是代理類UserNoInterface$$EnhancerByCGLIB$$b3361405的FastClass的invoke方法.
//=================被代理對象的fastclass的invoke方法
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        UserNoInterface var10000 = (UserNoInterface)var2;
        int var10001 = var1;

        try {
            switch(var10001) {
            case 0:
                var10000.doSomething();
                return null;
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

//=================代理對象的fastclass的invoke方法
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        b3361405 var10000 = (b3361405)var2;
        int var10001 = var1;
        try {
            switch(var10001) {
          
            case 7:
                var10000.doSomething();
                return null;
            case 15:
                var10000.CGLIB$doSomething$0();
                return null;
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }
		
複製代碼

咱們看到若是調用了MethodProxy.invoke() 會調用被代理對象的fastclass的invoke方法,會調用var10000.doSomething()。而var10000其實就是代理對象,這樣就出現了死循環。 這也是網上爲啥說的不能在此處使用MethodProxy.invoke()的緣由。

看看最終的調用鏈:

代理對象.doSomething()--->攔截器.intercept()--->MethodProxy.invokeSuper(sub, objects)--->代理類的FastClass類對象的.invoke()--->代理對象的CGLIB$doSomething$0()方法--->目標對象的方法doSomething()

小結:

  • CGLB動態代理能夠代理沒有實現接口的類
  • CGLB動態代理經過Enhancer 實現代理功能
  • CGLB動態代理生成一個目標對象的子類。

總結:

  • 靜態代理從源頭解決代理問題
  • 動態代理從運行使用時解決代理問題。
  • JDK動態代理經過InvocationHandler實現攔截。
  • GCLB經過MethodInterceptor實現攔截。

重點

1.JDK動態是經過反射來實現的。兩個要素是Proxy+InvocationHandler

  • Proxy 用於建立代理,而且內存中建立的類也繼承Proxy
  • InvocationHandler加強器,實現對目標方法的加強。

2.CGLB動態代理:兩要素Enhancer + MethodInterceptor(CallBack)

  • Enhancer建立代理
  • MethodInterceptor 加強器, 實現對目標方法加強。

3.JDK代理與CGLB動態代理都在內存中生成新的字節碼,最終仍是落在Class對象上,

要素是手段,字節碼纔是目的。JVM並不關心你java代碼,他關心的是在內存中可被使用的字節碼。

歡迎你們關注個人公衆號【源碼行動】,最新我的理解及時奉送。

在這裏插入圖片描述
相關文章
相關標籤/搜索