What Is the JDK com.sun.proxy.$Proxy Class? – 什么是 JDK com.sun.proxy.$Proxy 类?

最后修改: 2022年 5月 20日

1. Overview


When we use the dynamic proxy, the JDK will dynamically generate a $Proxy class. Usually, the fully qualified class name of this $Proxy class is somewhat similar to com.sun.proxy.$Proxy0. As the Java Documentation says, the “$Proxy” is a reserved name prefix for proxy classes.

当我们使用动态代理时,JDK将动态地生成一个$Proxy类。通常,这个$Proxy类的完全限定类名与com.sun.proxy.$Proxy0有些类似。正如Java 文档所说,”$Proxy “是代理类的一个保留名称前缀。

In this tutorial, we’re going to explore this $Proxy class.


2. The $Proxy Class


Before getting started, let’s distinguish between the java.lang.reflect.Proxy class and $Proxy class. The java.lang.reflect.Proxy is a JDK built-in class. And, in contrast, the $Proxy class is dynamically generated at runtime. From a class hierarchy perspective, the $Proxy class inherits the java.lang.reflect.Proxy class.


2.1. A Dynamic Proxy Example


To have a basis for discussion, let’s define two interfaces: BasicOperation and AdvancedOperation. The BasicOperation interface contains the add and subtract methods:


public interface BasicOperation {
    int add(int a, int b);

    int subtract(int a, int b);

And, the AdvancedOperation interface has the multiply and divide methods:


public interface AdvancedOperation {
    int multiply(int a, int b);

    int divide(int a, int b);

To get a newly generated proxy class – the $Proxy class – we can invoke the Proxy::getProxyClass method:


ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?>[] interfaces = {BasicOperation.class, AdvancedOperation.class};
Class<?> proxyClass = Proxy.getProxyClass(classLoader, interfaces);

However, the above proxyClass only exists in a running JVM and we cannot directly view its class members.


2.2. Dump $Proxy Class


For close scrutiny of this $Proxy class, we’d better dump it to disk. When using Java 8, we can specify the “sun.misc.ProxyGenerator.saveGeneratedFiles” option on the command line:

为了仔细检查这个$Proxy类,我们最好把它转储到磁盘。当使用Java 8时,我们可以在命令行中指定”sun.misc.ProxyGenerator.saveGeneratedFiles“选项。


Or we can set this option by invoking the System::setProperty method:


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

In Java 9 and later versions, we should use the “jdk.proxy.ProxyGenerator.saveGeneratedFiles” option instead. Why is there such a difference? Because of the Java Module System, the package of the ProxyGenerator class has changed. In Java 8, the ProxyGenerator is in the “sun.misc” package; however, since Java 9, the ProxyGenerator has moved into the “java.lang.reflect” package.

在Java 9及以后的版本中,我们应该使用”jdk.proxy.ProxyGenerator.saveGeneratedFiles“选项代替。为什么会有这样的区别?因为Java模块系统,ProxyGenerator类的包已经改变了。在 Java 8 中,ProxyGenerator 在”sun.misc“包中;但是,自 Java 9 起,ProxyGenerator 已移至”java.lang.reflect“包中。

In case we still don’t know which option is suitable, we can look up the saveGeneratedFiles field of the ProxyGenerator class to determine the correct one.


Be careful here: The ProxyGenerator class reads this property only once. And, this implies that the System::setProperty method will have no effect after the JVM has explicitly or implicitly produced any $Proxy classes. To be specific, invoking the Proxy::getProxyClass or Proxy::newProxyInstance method will explicitly generate the $Proxy class. On the other hand, when we read annotations, especially within the Unit Test framework, the JVM will implicitly or automatically generate the $Proxy class to represent annotation instances.


The exact location of the dumped class file is directly related to its fully qualified class name. For example, if the newly generated class name is “com.sun.proxy.$Proxy0“, then the dumped class file will be “com/sun/proxy/$Proxy0.class” in the current directory:



2.3. The Members of $Proxy Class


Now, it’s time to inspect the class members of this generated $Proxy class.


Let’s first inspect the class hierarchy. The $Proxy0 class has java.lang.reflect.Proxy as its superclass, which implicitly explains why dynamic proxy only supports interfaces. Also, the $Proxy0 class implements our previously defined BasicOperation and AdvancedOperation interfaces:


public final class $Proxy0 extends Proxy implements BasicOperation, AdvancedOperation

For readability, we have changed the field names of the $Proxy0 class into more meaningful ones. The hashCodeMethod, equalsMethod, and toStringMethod fields trace back to the Object class; the addMethod and subtractMethod fields are related to the BasicOperation interface; the multiplyMethod and divideMethod fields map to the AdvanceOperation interface:


private static Method hashCodeMethod;
private static Method equalsMethod;
private static Method toStringMethod;
private static Method addMethod;
private static Method subtractMethod;
private static Method multiplyMethod;
private static Method divideMethod;

Finally, the methods defined in the $Proxy0 class follow the same logic: All their implementations delegate to the InvocationHandler::invoke method. And, the $Proxy0 class will get an InvocationHandler instance from its constructor:


public $Proxy0(InvocationHandler handler) {

public final int hashCode() {
    try {
        return (Integer) super.h.invoke(this, hashCodeMethod, (Object[]) null);
    catch (RuntimeException | Error ex1) {
        throw ex1;
    catch (Throwable ex2) {
        throw new UndeclaredThrowableException(ex2);

3. How Proxy Works


After we have inspected the $Proxy class itself, it’s time to go one step further: how to generate the $Proxy class and how to load the $Proxy class? The key logic lies in the java.lang.reflect.Proxy and ProxyGenerator classes.


As new Java versions are released, the implementation details of the Proxy and ProxyGenerator classes continue to evolve. Roughly speaking, the ProxyGenerator is responsible for generating the $Proxy class’ byte array, and the Proxy class is responsible for loading this byte array into the JVM.


Now, let’s use Java 8, Java 11, and Java 17 for our discussion because they are LTS (Long-Term Support) editions.

现在,让我们用Java 8、Java 11和Java 17来讨论,因为它们是LTS(长期支持)版本。

3.1. Java 8

3.1.Java 8

In Java 8, we can describe the $Proxy class generation process in five steps:

在Java 8中,我们可以用五个步骤描述$Proxy类的生成过程。

The Proxy::getProxyClass or Proxy::newProxyInstance method is our starting point — either one will invoke the Proxy::getProxyClass0 method. And, the Proxy::getProxyClass0 method is a private method and will further invoke the ProxyClassFactory::apply method.


The ProxyClassFactory is a static nested class defined in the Proxy class. And, its apply method figures out the package name, class name, and access flags of the upcoming class. Then, the apply method will invoke the ProxyGenerator::generateProxyClass method.


In Java 8, the ProxyGenerator class is a public class defined in the “sun.misc” package. It has migrated to the “java.lang.reflect” package since Java 9. And, the generateProxyClass method will create a ProxyGenerator instance, invoke its generateClassFile method whose responsibility is bytecode generation, optionally dump the class file, and return the resulting byte array.

在Java 8中,ProxyGenerator类是一个定义在”sun.misc“包中的public类。而且,generateProxyClass方法将创建一个ProxyGenerator实例,调用其generateClassFile方法,该方法的职责是生成字节码,可以选择转储类文件,并返回生成的字节数。

After the bytecode generation succeeds, the Proxy::defineClass0 method is responsible for loading that byte array into the running JVM. Finally, we get a dynamically generated $Proxy class.


3.2. Java 11

3.2、Java 11

Compared with the Java 8 edition, Java 11 has introduced three major changes:

与Java 8版本相比,Java 11引入了三个主要变化

  • The Proxy class adds a new getProxyConstructor method and a static nested ProxyBuilder class
  • For Java Module System, the ProxyGenerator has migrated to the “java.lang.reflect” package and become a package-private class
  • To load the generated byte array into the JVM, the Unsafe::defineClass comes into play



3.3. Java 17

3.3、Java 17

Compared with the Java 11 edition, Java 17 has two major changes:

与Java 11版相比,Java 17有两个主要变化

  • From the implementation perspective, the ProxyGenerator class utilizes the JDK built-in ASM to do the bytecode generation
  • The JavaLangAccess::defineClass method is responsible for loading the generated bytecode into the JVM



4. Annotation Using Proxy


In Java, an annotation type is a special kind of interface type. But, we may be wondering how to create an annotation instance. In fact, we don’t need to. When we use the Java Reflection API to read an annotation, the JVM will dynamically generate a $Proxy class as the annotation type’s implementation:

在Java中,注解类型是一种特殊的接口类型。但是,我们可能想知道如何创建一个注解实例。事实上,我们不需要这样做。当我们使用Java Reflection API来读取一个注解时,JVM将动态地生成一个$Proxy类作为注解类型的实现

FunctionalInterface instance = Consumer.class.getDeclaredAnnotation(FunctionalInterface.class);
Class<?> clazz = instance.getClass();

boolean isProxyClass = Proxy.isProxyClass(clazz);

In the above code snippet, we use the Consumer class to get its FunctionalInterface instance, then get the instance’s class, and finally, use the Proxy::isProxyClass method to check whether the class is a $Proxy class.


5. Conclusion


In this tutorial, we first introduced a dynamic proxy example, then dumped the generated $Proxy class and inspected its members. To go one step further, we explained how the Proxy and ProxyGenerator classes work together to generate and load the $Proxy class in different Java versions. Finally, we mentioned that an annotation type is also implemented by using the $Proxy class.


As usual, the source code for this tutorial can be found over on GitHub.