JDK動態(tài)代理解析

要了解JDK的動態(tài)代理,我們首先要知道為什么需要動態(tài)代理,而不是普通的靜態(tài)代理

先看一個簡單的靜態(tài)代理

  • 被代理的接口
public interface HelloWorld {

    void sayHello(String name);
}
  • 被代理實現(xiàn)類
public class HelloWorldImpl implements HelloWorld {

    public void sayHello(String name) {
        System.out.println("hello nice world: " + name);
    }
}
  • 代理類 & 測試類
**
 * @author wangjn
 * @description 普通代理
 * @date Created on 2017/12/26.
 */
public class HelloWorldProxy {
    private HelloWorld helloWorld;

    HelloWorldProxy (HelloWorld helloWorld) {
        this.helloWorld = helloWorld;
    }

    void sayHello() {
        System.out.println("Before sample proxy---->");
        helloWorld.sayHello("wangjn");
        System.out.println("After sample proxy---->");
    }

    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorldImpl();
        HelloWorldProxy proxy = new HelloWorldProxy(helloWorld);
        proxy.sayHello();
    }
}

執(zhí)行測試類,輸出:

Before sample proxy----> hello nice world: wangjn After sample proxy---->

可以看到,這是一個很簡單的代理模式,我們寫一個這樣的類也很快。可問題是,如果我們有十幾二十個類似HelloWorld這樣的業(yè)務(wù)邏輯需要代理,我們就需要寫十幾二十個代理類,這是無法避免的且無法忍受的 由此引申出動態(tài)代理

動態(tài)代理的實現(xiàn)

實現(xiàn)一個動態(tài)代理也非常簡單

  • 動態(tài)代理類 實現(xiàn) InvocationHandler 接口
public class HelloWorldInvocationHandler implements InvocationHandler{
    private Object target;

    public HelloWorldInvocationHandler(Object target) {
        this.target = target;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before invocation---->");
        Object retVal = method.invoke(target, args);
        System.out.println("<----After invocation");
        return retVal;
    }
}
  • 測試類
public class JDKDynamicProxyTest {

    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        HelloWorldInvocationHandler handler = new HelloWorldInvocationHandler(new HelloWorldImpl());
        HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(
                JDKDynamicProxyTest.class.getClassLoader(),
                new Class[]{HelloWorld.class},
                handler);
        proxy.sayHello("wangjn");
    }
}

運行結(jié)果:

Before invocation----> hello nice world: wangjn <----After invocation

主要實現(xiàn)邏輯在 Proxy.newProxyInstance中

  • Proxy.newProxyInstance 方法源碼
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }

    /*
     * Look up or generate the designated proxy class.
     * 生成Class 文件的重要代碼,這里不深究是如何生成的
     */
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
            // 獲取生成的class的構(gòu)造方法 參數(shù)類型為 InvocationHandler.class
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        // 調(diào)用參數(shù)為 InvocationHandler h 的構(gòu)造方法創(chuàng)建實例
        return cons.newInstance(new Object[]{h});
    } catch (IllegalAccessException|InstantiationException e) {
        throw new InternalError(e.toString(), e);
    } catch (InvocationTargetException e) {
        Throwable t = e.getCause();
        if (t instanceof RuntimeException) {
            throw (RuntimeException) t;
        } else {
            throw new InternalError(t.toString(), t);
        }
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString(), e);
    }
}

看到這里,我們肯定想要看一下 Class<?> cl = getProxyClass0(loader, intfs) 生成的class文件長什么樣吧..

那我們現(xiàn)在就來看一下 這個動態(tài)生成的class長什么樣

  • $Proxy0 反編譯查看
public final class $Proxy0 extends Proxy implements HelloWorld {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(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 sayHello(String var1) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    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.taromilkbread.bird.HelloWorld").getMethod("sayHello", new Class[]{Class.forName("java.lang.String")});
            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());
        }
    }
}

看了動態(tài)生成的class,疑惑瞬間解了一大半

  • $Proxy0 實現(xiàn)了HelloWorld接口,且繼承了Proxy 類。
  • 并且構(gòu)造函數(shù)的參數(shù)就是 InvocationHandler h(我們自己實現(xiàn)的HelloWorldInvocationHandler代理類),跟Proxy中的newProxyInstance方法終于對接上了!
  • 每個 Method 也都在靜態(tài)塊里進行了初始化..

不過JDK動態(tài)代理有致命弱點,因為java不允許多繼承,而動態(tài)生成的class 卻繼承了Proxy類! 只能是實現(xiàn)了接口的類才可以被代理

不過好在之后已經(jīng)有了解決方案.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容