1. Overview
1.概述
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.
在本教程中,我们将探讨这个$Proxy类。
2. The $Proxy Class
2.$Proxy类
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.
在开始之前,让我们区分一下java.lang.reflect.Proxy类和$Proxy类的区别。java.lang.reflect.Proxy是一个JDK内置类。而与此相反,$Proxy类是在运行时动态生成的。从类的层次结构来看,$Proxy类继承了java.lang.reflect.Proxy类。
2.1. A Dynamic Proxy Example
2.1.一个动态代理的例子
To have a basis for discussion, let’s define two interfaces: BasicOperation and AdvancedOperation. The BasicOperation interface contains the add and subtract methods:
为了有一个讨论的基础,我们来定义两个接口。BasicOperation和AdvancedOperation。BasicOperation接口包含add和subtract方法。
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:
而且,AdvancedOperation接口有multiply和divide方法。
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:
为了得到一个新生成的代理类–$Proxy类–我们可以调用Proxy::getProxyClass方法。
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.
然而,上述proxyClass只存在于运行中的JVM中,我们不能直接查看其类成员。
2.2. Dump $Proxy Class
2.2.抛弃$Proxy类
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“选项。
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
Or we can set this option by invoking the System::setProperty method:
或者我们可以通过调用System::setProperty方法来设置这个选项。
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.
如果我们还是不知道哪个选项合适,我们可以查找saveGeneratedFiles类的saveGeneratedFiles字段来确定正确的选项。
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.
这里要小心。ProxyGenerator类只读取一次该属性。而且,这意味着在JVM显式或隐式地产生了任何$Proxy类之后,System::setProperty方法将不会产生任何影响。具体来说,调用Proxy::getProxyClass或Proxy::newProxyInstance方法将显式生成$Proxy类。另一方面,当我们读取注解时,特别是在单元测试框架内,JVM将隐含地或自动地生成$Proxy类来代表注解实例。
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:
转储类文件的确切位置与它的完全限定类名直接相关。例如,如果新生成的类名是”com.sun.proxy.$Proxy0“,那么转储的类文件将是当前目录下的”com/sun/proxy/$Proxy0.class“。
2.3. The Members of $Proxy Class
2.3.$Proxy类的成员
Now, it’s time to inspect the class members of this generated $Proxy class.
现在,是时候检查这个生成的$Proxy类的成员了。
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:
让我们首先检查一下类的层次结构。$Proxy0类将java.lang.reflect.Proxy作为其超类,这隐含地解释了为什么动态代理只支持接口。另外,$Proxy0类实现了我们之前定义的BasicOperation和AdvancedOperation接口。
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:
为了便于阅读,我们将$Proxy0类的字段名改为更有意义的名字。hashCodeMethod、equalsMethod和toStringMethod字段可以回溯到Object类。addMethod和subtractMethod字段与BasicOperation接口有关;multiplyMethod和divideMethod字段映射到AdvanceOperation>接口。
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:
最后,定义在$Proxy0类中的方法也遵循同样的逻辑。它们的所有实现都委托给InvocationHandler::invoke方法。而且,$Proxy0类将从其构造函数中获得一个InvocationHandler实例。
public $Proxy0(InvocationHandler handler) {
super(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
3.代理人如何工作
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.
在我们检查了$Proxy类本身之后,是时候更进一步了:如何生成$Proxy类以及如何加载$Proxy类?关键的逻辑在于java.lang.reflect.Proxy和ProxyGenerator类中。
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.
随着新的Java版本的发布,Proxy和ProxyGenerator类的实现细节也在不断发展。粗略地说,ProxyGenerator负责生成$Proxy类的字节数组,而Proxy类则负责将这个字节数组加载到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.
ProxyClassFactory是一个定义在Proxy类中的静态嵌套类。而且,它的apply方法会找出即将到来的类的包名、类名和访问标志。然后,apply方法将调用ProxyGenerator::generateProxyClass方法。
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.
在字节码生成成功后,Proxy::defineClass0方法负责将该字节数组加载到运行的JVM中。最后,我们得到一个动态生成的$Proxy类。
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
4.使用代理的注释
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);
assertTrue(isProxyClass);
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.
在上面的代码片段中,我们使用Consumer类来获取其FunctionalInterface实例,然后获取该实例的类,最后,使用Proxy::isProxyClass方法来检查该类是否是$Proxy类。
5. Conclusion
5.总结
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.
在本教程中,我们首先介绍了一个动态代理的例子,然后转储了生成的$Proxy类并检查了其成员。为了更进一步,我们解释了Proxy和ProxyGenerator类如何协同工作,在不同的Java版本中生成和加载$Proxy类。最后,我们提到一个注解类型也是通过使用$Proxy类来实现的。
As usual, the source code for this tutorial can be found over on GitHub.
像往常一样,本教程的源代码可以在GitHub上找到超过。