【Java】JDK 动态代理源码解析

Java 提供给了我们一个非常方便的动态代理类 Proxy,让我们今天来研究一下它的实现原理,以及为什么动态代理会存在性能问题。

代理对象的创建

我们往往通过 newProxyInstance(ClassLoader, Class<?>[], InvocationHandler) 方法进行代理对象的创建,它传递了三个参数:loaderinterfaces 以及 h

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) 
                                      throws IllegalArgumentException {
    Objects.requireNonNull(h);
    final Class<?>[] intfs = interfaces.clone();
    // 通过 SercurityManager 进行对即将代理的类的 PackageAccess 的权限检测
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }
    // 尝试获取或创建代理类
    Class<?> cl = getProxyClass0(loader, intfs);
    try {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        // 对非public类通过AccessController将Accessible设置为true
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
        }
        // 以 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);
    }
}

这里主要是如下的几步:

  1. 通过 SecurityManager 对即将代理的类的 package access 进行检测。
  2. 通过 getProxyClass0 尝试获取代理 Class 类。
  3. 对非 public 的类,通过 AccessControllerAccessible 设置为 true
  4. InvokeHandler 作为参数调用代理类的构造函数构造对象。

显然,动态代理的关键想必就是在 getProxyClass0 这个方法中了,可以大胆猜测一下这个代理类中的任何方法的调用都会通过传递进去的 InvokeHandler 进行代理。

我们看到 getProxyClass0

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
    // 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);
}

很奇怪,这里竟然只有一个从 proxyClassCache.get 的调用来获取缓存中的代理 Class,刚开始看到这里的时候以为 proxyClassCache 是个 Map 导致非常困惑。之后看到了上面的注释:

如果代理类已经被给定的 ClassLoader 实现过了,则从缓存中直接 copy 一份拿出,否则它会通过代理类的工厂 ProxyClassFactory 进行创建

然后再仔细一看,原来 proxyClassCache 是一个 WeakCache 对象😂,那看来对代理 Class 对象的创建就在它的 get 方法中实现了。

WeakCache 缓存

数据结构

首先我们来了解一下 WeakCache 究竟是用来干什么的,我们先看到它内部的数据结构:

final class WeakCache<K, P, V> {
    private final ReferenceQueue<K> refQueue
        = new ReferenceQueue<>();
    // the key type is Object for supporting null key
    private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
        = new ConcurrentHashMap<>();
    private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
        = new ConcurrentHashMap<>();
    private final BiFunction<K, P, ?> subKeyFactory;
    private final BiFunction<K, P, V> valueFactory;
}

可以看到,这里有如下的几个成员变量:

  • refQueue:引用队列。
  • map:一个 ConcurrentHashMap,里面的 value 也是 ConcurrentHashMap,它们的 key 均为 Object,用来支持为 null 的 key,而 value 则是 Supplier 类。
  • reverseMap:一个 keySuppliervalueBooleanConcurrentHashMap
  • subKeyFactory:用于生成 subKey 的工厂,也就是 map 中的 Map 的 key
  • valueFactory:用于生成 value 对象的工厂,既然我们的 Classvalue,这里想必就是 ProxyClassFactory 了。

果然,看到 proxyClassCache 的声明处:

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

可以看到,它的 subKeyFactoryKeyFactory 类,而 valueFactoryProxyClassFactory

get

我们接着看看它的 get 方法究竟做了什么:

public V get(K key, P parameter) {
    Objects.requireNonNull(parameter);

    expungeStaleEntries();
    // 通过 CacheKey.valueOf 将 key 转换为了存在缓存中的 CacheKey
    Object cacheKey = CacheKey.valueOf(key, refQueue);

    // 对 map 中的 value Map 进行获取,采用了懒创建的思路,这里还对并发问题进行了考虑
    ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
    if (valuesMap == null) {
        ConcurrentMap<Object, Supplier<V>> oldValuesMap
            = map.putIfAbsent(cacheKey,
                              valuesMap = new ConcurrentHashMap<>());
        if (oldValuesMap != null) {
            valuesMap = oldValuesMap;
        }
    }
    // 通过 subKeyFactory 创建 subKey 并获取对应的 Supplier
    Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
    Supplier<V> supplier = valuesMap.get(subKey);
    Factory factory = null;
    while (true) {
            // 通过 supplier.get 获取对应的 value
        if (supplier != null) {
            // supplier might be a Factory or a CacheValue<V> instance
            V value = supplier.get();
            if (value != null) {
                return value;
            }
        }
        // 如果缓存中没有对应的 supplier,可能是被 CacheValue 清理了,或者 Factory 还未 install
        if (factory == null) {
            factory = new Factory(key, parameter, subKey, valuesMap);
        }
        if (supplier == null) {
                // 将 factory 放入 valuesMap 中,并将其设置为 supplier
            supplier = valuesMap.putIfAbsent(subKey, factory);
            if (supplier == null) {
                // successfully installed Factory
                supplier = factory;
            }
            // 如果此时已经有 supplier,则用它继续重试
        } else {
            if (valuesMap.replace(subKey, supplier, factory)) {
                // Factory 成功 install,则将其设置为 supplier 并重试
                supplier = factory;
            } else {
                // retry with current supplier
                supplier = valuesMap.get(subKey);
            }
        }
    }
}

这里看上去代码比较长,其实结构是比较清晰的:

  1. 首先通过 CacheKeykey 转变为在 map 中的 key,这里的 key 实际上是我们的 ClassLoader,也就是这里先用 ClassLoader 进行了一遍筛选,是第一级缓存。
  2. map 中的 valuesMap 进行获取,若还没有则对其进行创建,这里生成的就是我们的第二级缓存 Map。
  3. 通过 subKeyFactory 创建 subKey 并获取对应的 Supplier,通过自定义的 subKeyFactory 进行创建,这个 subKey 用于通过第二级缓存获取真正的数据。
  4. 不断循环,对 Supplier 进行创建并调用其 get 方法获取需要的 value。这里创建 Supplier 的具体逻辑感兴趣的可以看看上面的代码及注释,最终创造出来的 Supplier 是一个 Factory 对象。

上面的代码中可以发现考虑到了非常多的并发安全性问题,用到了很多 CAS 操作进行值的 put,值得我们学习。

并且可以发现,WeakCache 中通过以 ClassLoaderkey 的第一级缓存,以及以自定义的 subKeyFactory 生成的 subKeykey 的第二级缓存,极大地加快了 Map 的查找效率,也是很值得我们学习的一个操作。

那么既然存在 valuesMap 中的 SupplierFactory 对象,让我们看看 Factory.get 方法:

@Override
public synchronized V get() { // serialize access
    // 再次检查当前的 Supplier 是不是自己
    Supplier<V> supplier = valuesMap.get(subKey);
    if (supplier != this) {

        return null;
    }
    // 通过 valueFactory.apply 方法创建需要的对象
    V value = null;
    try {
        value = Objects.requireNonNull(valueFactory.apply(key, parameter));
    } finally {
        if (value == null) { // remove us on failure
            valuesMap.remove(subKey, this);
        }
    }
    // ... 省略
    return value;
}

可以看到,它实际上就是通过 valueFactory 进行了 value 的创建,并将其返回。

那么我们对 WeakCache 类进行一下总结:

  • WeakCache 中只有 get 方法,当缓存中没有存在对应的 value 时,它会通过创建时传入的 valueFactoryvalue 进行创建。

  • 它通过以 ClassLoaderkey 的第一级缓存以及以自定义的 subKeyFactory 生成的 subKeykey 的第二级缓存,这样的二级缓存机制极大的加快了我们对 Map 的查找效率。

  • WeakCache 并没有通过加锁来保证线程安全,而是将它的线程安全交给了 ConcurrentHashMap 来保证,并且通过大量的 CAS 操作来保证对 Map 操作的安全性。

代理 Class 的创建

ProxyClassFactory

接着我们看看 ProxyClassFactory 是如何生成我们的代理类 Class 对象的:

// 代理类名的前缀
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) {
        // 遍历接口列表通过 Class.forName 加载 Class 对象
        Class<?> interfaceClass = null;
        try {
            interfaceClass = Class.forName(intf.getName(), false, loader);
        } catch (ClassNotFoundException e) {
        }
        // ... 异常检查
    }
    String proxyPkg = null;     // package to define proxy class in
    int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    // 如果存在非 public 的接口,记录非 public 接口的包名,后面会将它们生成的代理类采用同样的 package access(这些接口必须来自同一个包)
    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");
            }
        }
    }
    // 没有非 public 的接口,用 sun.proxy 作为其包名
    if (proxyPkg == null) {
        proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
    }
    // 构造代理类的名字
    long num = nextUniqueNumber.getAndIncrement();
    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    // 通过 ProxyGenerator.generateProxyClass 生成代理类 Class
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces, accessFlags);
    try {
            // 通过 defineClass0 声明 Class 并返回
        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());
    }
}

可以看到,ProxyClassFactory 中主要是对代理类名的确定,它主要经历了下面的步骤:

  1. 遍历接口列表,通过 Class.forName 加载接口对应的 Class 对象。
  2. 若存在非 public 的接口,用它们的包名作为所有代理类的包名,并且它们的包访问权限也会变成非 public。
  3. 若没有非 public 的接口,使用 sun.proxy 作为包名。
  4. proxyPkg + proxyClassNamePrefix + num 的格式构建代理类名
  5. 调用 ProxyGenerator.generateProxyClass 创建代理 Class 的 Class 文件。
  6. 通过 defineClass0 定义 Class 并返回。

ProxyGenerator

显然,Class 文件生成的关键位于ProxyGenerator.generateProxyClass 中,我们首先看到 ProxyGeneratory.generateProxyClass

我的 IDEA 只能查看到 Class 文件,如果想看 Java 文件的读者可以到这里查看:ProxyGenerator.java

public static byte[] generateProxyClass(final String name,
                                        Class<?>[] interfaces,
                                        int accessFlags) {
    ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
    final byte[] classFile = gen.generateClassFile();

    if (saveGeneratedFiles) {
        java.security.AccessController.doPrivileged(
        new java.security.PrivilegedAction<Void>() {
            public Void run() {
                try {
                    int i = name.lastIndexOf('.');
                    Path path;
                    if (i > 0) {
                        Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));
                        Files.createDirectories(dir);
                        path = dir.resolve(name.substring(i+1, name.length()) + ".class");
                    } else {
                        path = Paths.get(name + ".class");
                    }
                    Files.write(path, classFile);
                    return null;
                } catch (IOException e) {
                    throw new InternalError(
                        "I/O exception saving generated file: " + e);
                }
            }
        });
    }

    return classFile;
}

可以看到,它实际上是构建了一个对应参数的 ProxyGenerator 对象,并调用了其 generateClassFile 方法:

private byte[] generateClassFile() {

    // 第一步,将类中的所有法组装为 ProxyMethod 对象

    // 对于 hashCode equals toString 方法,首先调用 addProxyMethod 从而将原本的方法用 Object 的实现进行覆盖
    addProxyMethod(hashCodeMethod, Object.class);
    addProxyMethod(equalsMethod, Object.class);
    addProxyMethod(toStringMethod, Object.class);

    // 对接口中其余的方法调用 addProxyMethod 添加代理方法
    for (Class<?> intf : interfaces) {
        for (Method m : intf.getMethods()) {
            addProxyMethod(m, intf);
        }
    }

    // 遍历所有生成的代理方法,确保它们的返回值是兼容的
    for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
        checkReturnTypes(sigmethods);
    }

    // 第二步,组装生成的Class文件的FieldInfo和MethodInfo (每个方法都会生成一个static的Method对象和对应的代理方法)
    try {
        methods.add(generateConstructor());

        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            for (ProxyMethod pm : sigmethods) {

                // 添加代理类的 static 字段
                fields.add(new FieldInfo(pm.methodFieldName,
                    "Ljava/lang/reflect/Method;",
                     ACC_PRIVATE | ACC_STATIC));

                // 添加代理方法
                methods.add(pm.generateMethod());
            }
        }

        methods.add(generateStaticInitializer());

    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception", e);
    }

        // 确保不会超过 65535 的限制
    if (methods.size() > 65535) {
        throw new IllegalArgumentException("method limit exceeded");
    }
    if (fields.size() > 65535) {
        throw new IllegalArgumentException("field limit exceeded");
    }

    // 第三步,写入 Class 文件

    // 对常量池进行验证
    cp.getClass(dotToSlash(className));
    cp.getClass(superclassName);
    for (Class<?> intf: interfaces) {
        cp.getClass(dotToSlash(intf.getName()));
    }

    // 常量池设置为已读
    cp.setReadOnly();

    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    DataOutputStream dout = new DataOutputStream(bout);

    try {
        // 写入MagicNumber
        dout.writeInt(0xCAFEBABE);
        // 写入minor版本号
        dout.writeShort(CLASSFILE_MINOR_VERSION);
        // 写入major版本号
        dout.writeShort(CLASSFILE_MAJOR_VERSION);
                // 写入常量池
        cp.write(dout);
                // 写入 accessFlags
        dout.writeShort(accessFlags);
        // 写入当前class索引
        dout.writeShort(cp.getClass(dotToSlash(className)));
                // 写入父类/接口索引
        dout.writeShort(cp.getClass(superclassName));
                // 写入接口个数
        dout.writeShort(interfaces.length);
        // 写入接口
        for (Class<?> intf : interfaces) {
            dout.writeShort(cp.getClass(
                dotToSlash(intf.getName())));
        }
        // 写入字段个数
        dout.writeShort(fields.size());
        // 写入字段
        for (FieldInfo f : fields) {
            f.write(dout);
        }
                // 写入方法个数
        dout.writeShort(methods.size());
        // 写入方法
        for (MethodInfo m : methods) {
            m.write(dout);
        }
                // 写入属性表,代理类不需要属性表(包含了代码)
        dout.writeShort(0);

    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception", e);
    }
        // 输出为byte数组
    return bout.toByteArray();
}

这里的代码非常非常非常长,但是结构其实非常清晰,并且官方几乎对每个操作都加上了注释,非常易于理解:

  1. 第一步,将类中的所有法组装为 ProxyMethod 对象
    1. 对于 hashCodeequalstoString 方法,首先调用 addProxyMethod 从而将原本的方法用 Object 的实现进行覆盖
    2. 对接口中其余的方法调用 addProxyMethod 添加代理方法。
    3. 遍历所有生成的代理方法,确保它们的返回值是兼容的。
  2. 第二步,组装生成的 Class 文件的 FieldInfoMethodInfo (每个方法都会生成一个 staticMethod 成员变量和对应的代理方法)
    1. 遍历所有 ProxyMethod,添加代理类的 static Method 对象字段,并添加对应的代理方法。
    2. 确保方法数、字段数不会超过 65535 的限制。
  3. 第三步,将所有内容写入 Class 文件(之前写 ClassDecoder 的时候写了很多类似的代码,很有感触哈哈,又复习了一遍 Class 文件的结构)
    1. 写入 MagicNumber
    2. 写入 minor 版本号
    3. 写入 major 版本号
    4. 写入常量池
    5. 写入 accessFlags
    6. 写入当前类的索引
    7. 写入父类/接口的索引
    8. 写入接口个数
    9. 写入接口
    10. 写入字段个数
    11. 写入字段
    12. 写入方法个数
    13. 写入方法
    14. 写入属性表,由于代理类不需要属性表,因此写入的是 0
    15. 输出为 byte 数组。

关于 ProxyGeneratoraddProxyMethod 等具体实现我们就不再关注了,实际上就是通过反射解析方法的参数等信息然后创建一个对应的方法。最后生成的代理类中的每个方法都会调用到 InvokeHandler.invoke 方法。

而最后的 defineClass0 方法是一个 native 方法,估计是调用到了 JVM 底层的方法,这里就不去关注了。

总结

JDK 中的动态代理是一套基于反射获取接口的信息,通过对 Class 文件按字节写入生成新的反射类文件并加载进 JVM 的动态代理框架。它有个很致命的特点就是只支持对接口的代理

一个代理对象的生成主要分两步:

  1. 通过 WeakCache 获取缓存的 Class 对象。其中 WeakCache 存在如下的特点:
    1. WeakCache 中只有 get 方法,当缓存中没有存在对应的 value 时,它会通过创建时传入的 valueFactoryvalue 进行创建。

    2. 它通过以 ClassLoaderkey 的第一级缓存以及以自定义的 subKeyFactory 生成的 subKeykey 的第二级缓存,这样的二级缓存机制极大的加快了我们对 Map 的查找效率。

    3. WeakCache 并没有通过加锁来保证线程安全,而是将它的线程安全交给了 ConcurrentHashMap 来保证,并且通过大量的 CAS 操作来保证对 Map 操作的安全性。
  2. 创建接口对应的 Class 代理类对象。

    1. 通过 ProxyClassFactory 先解析接口的信息并生成对应的代理类名。
    2. 通过 ProxyGenerator 对类文件的信息进行组装并输出对应的 Class 文件,主要分为以下三步:
      1. 第一步,将类中的所有法组装为 ProxyMethod 对象。
      2. 第二步,组装生成的 Class 文件的 FieldInfoMethodInfo
      3. 第三步,将所有内容写入 Class 文件。

最终,代理类中所有的方法的调用都会调用到 InvocationHandler.invoke,从而实现用户对接口的代理。

参考资料

ProxyGenerator.java

JDK动态代理——WeakCache缓存的实现机制

点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注

%d 博主赞过: