Java基礎-瞭解一下jdk的動態代理的本質

先簡短的回顧下jdk動態代理用法

1.定義一個基礎的接口

public interface Service {
    void print();
}
複製代碼

2.簡單的實現一下接口

public class MyService implements Service {
    @Override
    public void print() {
        System.out.println("this is print");
    }
}
複製代碼

3.實現jdk的InvocationHandler接口

public class MyHandler implements InvocationHandler {

    private Service service;

    public MyHandler(Service service){
        this.service = service;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("this is before!");
        Object result = method.invoke(service, args);
        System.out.println("this is after!");
        return result;
    }
}
複製代碼

4.調用Proxy類實現動態加強

public static void main(String[] args) {
        Service service = new MyService();
        Service proxyInstance = (Service) Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{Service.class}, new MyHandler(service));
        proxyInstance.print();
    }
複製代碼

若是不出意外的話,控制檯會打印出以下信息java

this is before!緩存

this is printbash

this is after!app

這說明咱們寫的方法,獲得了加強!ide

JDK動態代理的原理

1.jdk動態代理的實質是什麼?

不知道你們有沒有想過,這行代碼函數

Proxy.newProxyInstance(Demo.class.getClassLoader(), new Class[]{Service.class}, new MyHandler(service));
複製代碼

返回的到底是個什麼東西?源碼分析

帶着疑問,咱們先使用反射打印一下類名試試。post

System.out.println(proxyInstance.getClass().getName());
複製代碼

獲得的結果爲ui

com.sun.proxy.$Proxy0
複製代碼

很顯然這個類並非咱們建立的。因此到這兒就應該想到了,動態代理實質,就是使用字節碼技術,從新生成了一個新類,來達到加強的效果。this

那麼加強的新類究竟是個怎樣的類呢?咱們來挖掘一下動態代理的源碼。

2.jdk動態代理源碼分析

分析源碼的時候我通常都是根據方法的參數和返回值,大體推敲一下方法的功能,這樣能夠快速找到關鍵方法。因此後面的代碼都是被我精簡過的,你們能夠對比着源碼閱讀。

首先進入newProxyInstance方法,在去除掉業務不相干代碼後以下:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
                                          
            Class<?> cl = getProxyClass0(loader, intfs);
            
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            
            return cons.newInstance(new Object[]{h});
    }
複製代碼
  1. 第一步經過getProxyClass0方法,得到了一個class對象cl
  2. 第二步得到了cl對象的構造函數,這個構造函數的參數是一個 InvocationHandler類型
  3. 第三步經過咱們傳入的InvocationHandler接口實現類h構造了cl對象的實例。

也就是新類就算這個cl,弄清除cl是啥,動態代理的原理咱們就基本弄懂了。因此跟着這個目標,進入getProxyClass0方法。

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        return proxyClassCache.get(loader, interfaces);
}
複製代碼

除去校驗的方法外,只剩下一行代碼。因此只能在進去看看。在進入以前,先看看proxyClassCache是啥。

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
        
    public WeakCache(BiFunction<K, P, ?> subKeyFactory,
                     BiFunction<K, P, V> valueFactory) {
        this.subKeyFactory = Objects.requireNonNull(subKeyFactory);
        this.valueFactory = Objects.requireNonNull(valueFactory);
    }
複製代碼

如今在進入proxyClassCache的get方法裏,進入get方法咋一看代碼有點多,其實上面都是緩存相關處理(緩存的話我建議等看完主流程再回頭看,那樣更有助於理解緩存),咱們先跳過,直接看關鍵代碼

while (true) {
            if (supplier != null) {
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    supplier = factory;
                }
            } 
        }
複製代碼

關鍵代碼經簡化後如上,這樣就能清晰的看出supplier就是new Factory(key, parameter, subKey, valuesMap)的實例。因此進入supplier的get方法。

public synchronized V get() { // serialize access
            try {
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } finally {
                if (value == null) { // remove us on failure
                    valuesMap.remove(subKey, this);
                }
            }
            return value;
        }
    }
複製代碼

進入get方法後,關鍵代碼就一句valueFactory.apply(key, parameter),而這個valueFactory就是上面在建立WeakCache時設置的ProxyClassFactory。因此進入ProxyClassFactory的apply方法。

在apply方法中,終於看到了建立代理類的關鍵方法

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
       proxyName, interfaces, accessFlags);
       return defineClass0(loader, proxyName,
                            proxyClassFile, 0, proxyClassFile.length);
複製代碼

其中proxyClassFile就是新類的字節碼,而defineClass0方法,就是加載這個新類。對於字節碼如何構造在這兒我就不深究了。感興趣的能夠本身研究。我更關心新類的結構。因此既然找到了新類的生成方法,咱們就將他打印到文件中瞧一瞧。

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                "ProxyService", new Class[]{Service.class}, Modifier.FINAL);
        OutputStream outputStream = new FileOutputStream(new File("d:/JdkProxy.class"));
        outputStream.write(proxyClassFile);
複製代碼

在d盤下找到這個文件,直接用idea打開。

final class ProxyService extends Proxy implements Service {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public ProxyService(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

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

    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 int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("study.demo.jdk_proxy.Service").getMethod("print");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}
複製代碼

不出意外就會看到如上代碼了。其實看到這個代碼後,jdk動態代理的本質內心就應該有底了。因此後面就不用再說下去了吧。

下一篇,分析一下cglib動態代理的本質

相關文章
相關標籤/搜索