上篇文章介绍了JDK的动态代理的用法,结尾处挖了个坑,这次文章就顺着两个问题来剖析JDK动态代理的源码。

首先来看第一个问题,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.
     */
    // 关键代码1:根据传入的接口列表创建动态代理类
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        // 关键代码2:获取动态代理类的构造方法
        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;
                }
            });
        }
        // 关键代码3:调用动态代理类的构造方法,传入InvocationHandler,创建动态代理实例
        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);
    }
}

其中最关键的代码是以下这行:

getProxyClass0(loader, intfs); 

这行代码传入的第一个参数是需要实现的接口的ClassLoader,第二个参数是需要实现的接口列表。

这个方法会先在缓存里面查找是否已经创建过该代理类,如果没有,则会动态创建一个代理类。

再深入一点取看 getProxyClass0 的源码的话,会发现它最终调用的是 ProxyClassFactory.apply() 方法来创建代理类,以下是完整代码,关键部分也加上了中文注释。

private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
    // prefix for all proxy class names
    // 动态代理类的类名前缀
    private static final String proxyClassNamePrefix = "$Proxy";

    // next number to use for generation of unique proxy class names
    // 原子计数器,用于动态代理类的类名后缀
    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.
             */
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            /*
             * Verify that the Class object actually represents an
             * interface.
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
             * Verify that this interface is not a duplicate.
             */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }

        String proxyPkg = null;     // package to define proxy class in
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

        /*
         * Record the package of a non-public proxy interface so that the
         * proxy class will be defined in the same package.  Verify that
         * all non-public proxy interfaces are in the same package.
         */
        // 根据接口的可见性,来决定动态代理的包名
        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");
                }
            }
        }

        if (proxyPkg == null) {
            // if no non-public proxy interfaces, use com.sun.proxy package
            proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
        }

        /*
         * Choose a name for the proxy class to generate.
         */
        long num = nextUniqueNumber.getAndIncrement();
        String proxyName = proxyPkg + proxyClassNamePrefix + num;

        /*
         * Generate the specified proxy class.
         */
        // 关键代码 1:创建动态代理类的字节码
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
            proxyName, interfaces, accessFlags);
        try {
            // 关键代码 2:定义动态代理类
            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());
        }
    }
}

其中最关键的代码是以下这句:

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

这个方法第一个参数是代理类的类名,第二个参数是需要实现的接口列表,第三个参数是访问控制Flag,返回的是一个byte数组。

返回的byte数组就是动态代理类的字节码,也就是说jdk创建了一个新的类。

下一行关键代码是:

defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);

这一句代码第一个参数是classLoader,第二个参数是类名,第三个参数是字节码,作用是动态地将刚才创建的字节码定义成了一个新的类,也就是说JDK在运行时生成了一个新类,是不是突然有了一股动态语言的味道了。


接下来回答第二个问题,即以下代码为什么会报 ClassCastException

Person proxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), Person.class.getInterfaces(), handler);

其实看过前面的源码以后,这个问题就已经非常明显了,因为 Proxy.newProxyInstance 返回的是一个新的类,这个类实现了Person类实现的所有接口,但是跟Person类不是同一个类,因此无法Cast成Person类。

通过IDE的调试功能我们可以更加直观地理解这个现象。

undefined

在下方的变量监控界面可以看到 proxy 变量的类名是 $Proxy12 ,这个 $Proxy12 就是JDK运行时动态创建出来的新类,并不是我们在编写代码时定义的。

$Proxy12 虽然实现了IPerson接口,但是由于跟Person类没有继承关系,因此如果将proxy对象cast到Person类的话,就会报错。

JDK动态代理的基础知识基本就是这些了。