1. 程式人生 > >從代理模式再出發!Proxy.newProxyInstance的祕密

從代理模式再出發!Proxy.newProxyInstance的祕密

上一節簡單介紹了一下ClassLoader,我們知道,在我們用Proxy.newProxyInstance實現動態代理的時候,有三個引數,第一個便是classloader,這一篇文章就是簡單看一下Proxy.newProxyInstance是如何實現動態代理的,因為我們後面可以以此為參考,實現自己的動態代理工具。

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

Proxy靜態方法newProxyInstance

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
    // 檢查 h 不為空,否則拋異常
        Objects.requireNonNull(h);
    // 對傳入的介面做安全檢查
final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } /* * 實現動態代理的核心方法,動態代理的思路便是生成一個新類,剛剛getProxyClass0便成為了生成新類 */
Class<?> cl = getProxyClass0(loader, intfs); /* * 根據生成的class通過反射獲取建構函式物件並生成代理類例項 */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } 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; } }); } 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); } }

proxyClassCache

getProxyClass0方法裡其實直接取proxyClassCache快取,proxyClassCache是一個代理類的快取變數,如果這個快取裡有這個代理類,就直接返回代理類,如果沒有,就會通過ProxyClassFactory建立代理物件。

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);

ProxyClassFactory

ProxyClassFactory是Proxy裡的一個內部類,用來生成代理類,apply()方法 最後呼叫ProxyGenerator.generateProxyClass()方法來完成生成位元組碼的操作,這裡不講它是如何生成的,我們只要知道它可以返回一個全新的class類,如果想看這個class類的內容,可以在測試類中這麼寫:

public class Test {
    public static void main(String[] args) {
//加入這一段可以在磁碟中生成 代理類,讓我們看到代理類的真面目        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        Sourceable source = new Source();
        Sourceable test = (Sourceable) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{Sourceable.class}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("test");
                if ("method".equals(method.getName())) {
                    return method.invoke(source, null);
                }
                return null;
            }
        });
        test.method();
    }

代理類 $Proxy0.class

可以看到$Proxy0 實現了我們給的介面Sourceable 並且繼承了Proxy,構造類要傳入我們使用newProxyInstance時用的InvocationHandler變數,並且在static靜態初始化中把Sourceable介面的各個方法Method初始化完成(包括equals,toString等方法),並且真正執行方法的時候實際上是執行InvocationHandler物件的invoke()方法(super.h就是儲存在父類Proxy中的InvocationHandler物件),各個方法的區別就是Method物件不同而已,所以真正方法的執行是在InvocationHandler的invoke()方法中完成的。 這也說明了代理邏輯 和 動態代理本身是程式碼分離的,程式設計師只需要關注好自己的代理邏輯就行,動態代理本身就交給jdk本身去處理。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.sun.proxy;

import cn.xxywithpq.proxy.jdkproxy.bean.Sourceable;
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 Sourceable {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;
    private static Method m4;

    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 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 method() throws  {
        try {
            super.h.invoke(this, m3, (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);
        }
    }

    public final void method1(int var1) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("cn.xxywithpq.proxy.jdkproxy.bean.Sourceable").getMethod("method");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m4 = Class.forName("cn.xxywithpq.proxy.jdkproxy.bean.Sourceable").getMethod("method1", Integer.TYPE);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

總結

在jdk動態代理中,美中不足就是整個設計都是針對介面做的代理,如果是普通的類,我們無法通過這個方式代理物件(通過生成的代理類也知道沒有介面是不行的),但是我們知道 通過拼接位元組碼生成新的類 自由度是十分大的,這也就啟示我們 設計不管是針對介面類還是普通類的代理類 是完全可行的,比如cglib框架就是通過拼接位元組碼來實現非介面類的代理。 後面會介紹如何 實現這種操作,我在自己的Simplify-Core專案中已經嘗試通過asm框架(位元組碼讀寫框架)實現代理操作。有興趣的可以看看。

專案地址:

https://github.com/lovejj1994/Simplify-Core