jvm原理(36)透過字節碼生成審視Java動態代理運作機制

我們在使用spring這類框架的時候,基於動態代理的使用,比如AOP,會使得開發更加靈活,那麼在字節碼的層面動態代理是什麼樣子的呢,生成出來的代理類結構是什麼,本次我們首先寫一個動態代理的例子,然後得到生成的動態代理類。
定義接口:

public interface SubJect {
    void request();
}

定義實現類:

public class RealSubJect implements SubJect {
    @Override
    public void request() {
        System.out.println("method calling");
    }
}

定義動態代理類:

public class DynamicSubject implements InvocationHandler {

    private Object sub;

    public DynamicSubject(Object obj){
        this.sub = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("befor method call");
        method.invoke(sub,args);
        System.out.println("after method call");
        return null;
    }
}

定義客戶端:

public class Client {
    public static void main(String[] args) {
        RealSubJect realSubJect = new RealSubJect();
        InvocationHandler invocationHandler = new DynamicSubject(realSubJect);
        SubJect subJect = (SubJect) Proxy.newProxyInstance(realSubJect.getClass().getClassLoader(),realSubJect.getClass().getInterfaces(),invocationHandler);
        subJect.request();
        System.out.println(subJect.getClass());//打印動態代理類的class
        System.out.println(subJect.getClass().getSuperclass()); //打印父類
    }
}

運行客戶端得到結果:

befor method call
method calling
after method call
class com.sun.proxy.$Proxy0
class java.lang.reflect.Proxy

那麼這個com.sun.proxy.$Proxy0是怎麼出來的呢,這個需要進入到Proxy.newProxyInstance()裏邊看一下他的邏輯:

Proxy.newProxyInstance()
  -->getProxyClass0()
     -->proxyClassCache.get()[通過ProxyClassFactory獲取]
        -->WeakCache.Factory.get()
          -->valueFactory.apply(key, parameter)
             -->Proxy.apply()
               -->byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
                  -->byte[] proxyClassFile = ProxyGenerator.generateClassFile();

ProxyGenerator是 sun.misc包裏邊的,我們得到的代碼是ide反編譯的結果,我們貼出來,var4是生成出來的字節數組,然後
saveGeneratedFiles是一個開關,如果爲true就會把class文件輸出到磁盤

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
    ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
    final byte[] var4 = var3.generateClassFile();//返回最終的字節數組
    if (saveGeneratedFiles) {
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                try {
                    int var1 = var0.lastIndexOf(46);
                    Path var2;
                    if (var1 > 0) {
                        Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                        Files.createDirectories(var3);
                        var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                    } else {
                        var2 = Paths.get(var0 + ".class");
                    }

                    Files.write(var2, var4, new OpenOption[0]);
                    return null;
                } catch (IOException var4x) {
                    throw new InternalError("I/O exception saving generated file: " + var4x);
                }
            }
        });
    }

    return var4;
}

saveGeneratedFiles的定義:

 private static final boolean saveGeneratedFiles =
 ((Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();

ok 我們設置一下sun.misc.ProxyGenerator.saveGeneratedFiles就可以得到class文件。下邊這行代碼放在Client main方法的第一行,提前設置。

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

運行Client,然後在我們的工程裏邊就會出現一個目錄
com.sun.proxy.$Proxy0
這個就是動態代理類:

package com.sun.proxy;

import com.twodragonlake.jvm.bytecode.SubJect;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements SubJect {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    /**
    構造方法
    這個構造方法在Proxy類的newProxyInstance方法裏邊,有一個地方是獲取代理類的構造器:
     Class<?> cl = getProxyClass0(loader, intfs); //代理類
     final Constructor<?> cons = cl.getConstructor(constructorParams);//獲取代理類的構造器
     return cons.newInstance(new Object[]{h});//實例化代理類;
     h是InvocationHandler,InvocationHandler是我們定義的DynamicSubject的接口,這個時候就會調用當前的這個構造方法
     動態代理類的父類Proxy持有InvocationHandler的引用,完成這個引用的賦值。
    */
    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
          /**
            調用Proxy的InvocationHandler的invoke方法,其實就是調用DynamicSubject的invoke方法,
            因爲DynamicSubject實現了InvocationHandler,m1是Object類的equals方法,new Object[]{var1}是equals的方法參數
            如果DynamicSubject重新了equals方法就會轉發到DynamicSubject的equals方法,否則就是調用Object的equals方法
          */
            return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    /**
    和equals方法道理了一樣
    */
    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void request() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    /**
    和equals方法道理了一樣
    */
    public final int hashCode() throws  {
        try {
            return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
          //類加載的時候,將Object類的是個方法拿出來
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.twodragonlake.jvm.bytecode.SubJect").getMethod("request");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

除了equals、toString、hashCode的其他的Object的方法,都不會得到代理類的轉發,原來是什麼樣子的,代理後也還是什麼樣子的,不會發生變化。
好,到目前爲止我們分析了動態代理類的源碼,那回到client的代碼:

  SubJect subJect = (SubJect) Proxy.newProxyInstance(realSubJect.getClass().getClassLoader(),realSubJect.getClass().getInterfaces(),invocationHandler);

subJect返回的是一個動態代理類,這個類的名字是Proxy0Proxy0,Proxy0的父類是Proxy,父類持有InvocationHandler的引用,也就是持有DynamicSubject的引用,同時Proxy0SubJectProxy0實現了SubJect接口,Proxy0裏邊生成了DynamicSubject裏邊所聲明的方法,並且轉發到了DynamicSubject裏邊。當我們調用subJect.request();
的時候就是調用了Proxy0.request();Proxy0.request();Proxy0將方法的調用通過父類Proxy持有的InvocationHandler的引用,即【super.h.invoke(this, m0, (Object[])null))】進行了轉發,轉發到DynamicSubject的invoke方法。所以說DynamicSubject的invoke方法的第一個參數是proxy,接下里就會打印:

befor method call
method calling
after method call

動態代理的優勢:
代理對象可以在沒有真實對象不存在的情況下提前生成代理對象,代理對象可以代理多種真實對象,而且jdk的動態代理是面向接口的。
cglib:
面向繼承的代理,子類可以重寫父類的實現,同時代理類可以調用父類的方法(jdk動態代理沒有這個優勢,因爲jdk動態代理是面向接口的)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章