Java 動態代理 理解

動態代理

這裏暫時只作JDK動態代理分析。動態代理應用普遍,例如AOP。java

clipboard.png

clipboard.png
clipboard.png
clipboard.png
clipboard.png

吐槽本身一下,設計的類,接口名不是很好。。anyway,大體就是這樣。根據規範,Mama類實現InvocationHandler接口,實現invoke方法。以後經過Proxy類的靜態方法newProxyInstance取得一個代理類實例eat(再次鄙視本身)。當eat.eat()時,調用了Mama的invoke方法。
很好奇,源碼是如何實現代理的。值得一提,JDK動態代理把equals(),toString(),hashCode()也代理了。app

源碼分析

clipboard.png

clipboard.png

clipboard.png
一路debug發現,最後調用了Proxy類下內部類ProxyClassFactory的apply方法,其中包含的下列代碼最終動態地建立了proxy class 文件函數

clipboard.png

這時咱們反編譯看看,最後編譯成功的proxy class源碼分析

public final class KidProxy extends Proxy implements Eat {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

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

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

    public final void eat() 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)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("com.ProxyDemo.Eat").getMethod("eat", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

經過構造函數KidProxy(InvocationHandler var1)初始化類instance,這個super(var1)便是Proxy的構造函數Proxy(InvocationHandler invocationHandler)便是靜態方法newProxyInstance傳過去的參數。具體的調用又是在newProxyInstance方法中的最後一句this

return cons.newInstance(new Object[]{h});

可知是經過反射實例化proxy對象的,一樣的在構造proxy class文件時,也是經過反射,經過其實現的interfaces的具體方法將須要實現的method寫入proxy class文件的。在記載class文件時,經過static構建method。spa

static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("com.ProxyDemo.Eat").getMethod("eat", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

其中m3就是我要代理的方法。具體調用的時候,經過將方法delegate給invocationHandler實例。debug

總結

JDK動態代理
優勢設計

  • 相比靜態代理,不用每代理一個類就得寫一個新的代理類。
    缺點代理

  • 只能代理實現了interface接口的類,由於java是單繼承,代理類已是Proxy類的子類了。實現代理沒有實現接口的類,還得靠ASM技術。code

相關文章
相關標籤/搜索