動態代理-JDK動態代理的內部實現

0.一個簡單的JDK動態代理的例子

建立代理類接口java

public interface ProxyInterface {
    public String proxy(String username);
}

建立接口的實現緩存

public class ProxyClass implements ProxyInterface {
    public String proxy(String username) {
        System.out.println("....processor....");
        return String.format("Hello Proxy : %s", username);
    }
}

實現一個InvocationHandlerapp

public class ProxyInvocationHandler implements InvocationHandler {
    private Object target;

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

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("---Before---Method Name:" + method.getName());

        Object retVal = method.invoke(target, args);

        System.out.println("---After---Return Value:" + retVal);
        return retVal;
    }
}

使用動態代理執行被代理類的方法ide

public class Main {
    public static void main(String[] args) {
        ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(new ProxyClass());

        ProxyInterface proxy = (ProxyInterface) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{ProxyInterface.class}, proxyInvocationHandler);

        proxy.proxy("sage");
    }
}

查看輸出結果函數

---Before---Method Name:proxy
....processor....
---After---Return Value:Hello Proxy : sage

1.JDK動態代理的源碼實現

    咱們從上面的代碼能夠看出,JDK動態代理的使用很是簡單,只須要調用Proxy.newProxyInstance方法便可,咱們就從這裏入手看一下JDK動態代理的源碼實現.下面的代碼,沒有特殊說明,都是在Proxy類中~性能

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);
        }

        /*
         * 查找或生成動態代理類.(JDK若是在緩存中已經找到生成的動態代理類,會直接返回)
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * 使用咱們的InvocationHandler做爲新生成的代理類的構造方法的參數,來獲取代理類的實例
         */
        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);
        }
    }

    在這裏最重要的地方就是註釋的兩個地方:ui

  • getProxyClass0
  • 使用InvocationHandler生成代理類的實例

1.1 getProxyClass0()

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        //檢查被代理類的接口數量,不能多於65535,問題是...誰會寫那麼多接口...
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        //JDK對生成的動態代理類進行了緩存,若是已經存在了相應的代理類,就直接返回,提升了性能
        return proxyClassCache.get(loader, interfaces);
}

    下面咱們來看一下proxyClassCache,具體的緩存方式我就不詳說了,就來看一下這個proxyClassCache是如何建立的吧,這裏有一個很重要的東西.this

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    這裏面使用了ProxyClassFactory來生成代理類,咱們來看一下這個類的源碼:ProxyClassFactory是Proxy的靜態內部類.spa

private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // 全部動態代理類的前綴都以 $Proxy 開頭
        private static final String proxyClassNamePrefix = "$Proxy";

        // 用於生成代理類名字的計數器,也就是說,全部代理類的名字模式都同樣,使用計數器來分別
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            //代理類的接口校驗邏輯,不是很重要....
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
            }

            //代理類的包名
            String proxyPkg = null;     
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            //若是接口不是public的,那麼生成代理類的包名和被代理類的包名相同
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            //若是在上面的邏輯中沒有設置生成代理類的包名,則指定默認值:com.sun.proxy.
            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            //計數器增長,生成代理類的類名
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            //生成代理類的核心邏輯,這段代碼木有開源,咱們一下子能夠反編譯看一下大概的內容
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

1.2 ProxyGenerator.generateProxyClass()

    由於這個類是沒有開源的,因此咱們就大體來看一下這個方法的內容:代理

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() {
                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), new String[0]);
                            Files.createDirectories(var3, new FileAttribute[0]);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class", new String[0]);
                        }

                        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:來判斷是否將生成的動態代理類保存到本地的變量,是經過環境變量來判斷的的.

sun.misc.ProxyGenerator.saveGeneratedFiles

2.經過實例查看生成的動態代理類

    還使用最開始的例子, 先在main方法的最開始配置環境變量:

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

    運行Main方法,而後進入com/sun/proxy包,查看一下生成的代理類:

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

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);//就是將InvocationHandler賦值給Proxy的h變量
    }

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

    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")});
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("test.sage.proxy.ProxyInterface").getMethod("proxy", new Class[]{Class.forName("java.lang.String")});
            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());
        }
    }
}

    觀察這個類的特色以下:

  • 生成的代理類繼承了Proxy類,同時實現了代理接口.這就說明了爲何JDK動態代理只支持接口的緣由,由於Java不支持多繼承.
  • 提供了一個構造函數,傳入InvocationHandler,並賦值給Proxy的h變量
  • 全部的方法,包括咱們本身的方法,都只是簡單的調用了invocationHandler的invoke方法.因此,咱們的InvocationHandler其實均可以不用調用咱們本身的方法,而執行別的邏輯.

3.總結

    經過上面的解析,咱們能夠總結出來:

  • JDK動態代理的類必須實現接口
  • 代理的加強類須要實現InvocationHandler
  • 經過Proxy.newProxyInstance來建立代理對象
  • 生成代理類的核心是:ProxyClassFactory,並經過saveGeneratedFiles來判斷是否將動態代理的類生成到本地
  • 生成的代理類的包路徑是com.sun.proxy,經過一個AtomicLong來進行類名的生成
  • 生成的代理類默認繼承了Proxy類,並以咱們自定義的InvocationHandler做爲構造參數
相關文章
相關標籤/搜索