1. 概述
使用动态代理时,JDK会动态生成一个名为$Proxy
的类。这个类的全限定名通常类似于com.sun.proxy.$Proxy0
。根据Java官方文档,"$Proxy"是代理类保留的名称前缀。
本文将深入探索这个神秘的$Proxy
类。
2. $Proxy类详解
先明确区分两个概念:
java.lang.reflect.Proxy
:JDK内置类$Proxy
类:运行时动态生成的类
从继承关系看,$Proxy
类继承自java.lang.reflect.Proxy
。
2.1 动态代理示例
定义两个接口作为基础:
public interface BasicOperation {
int add(int a, int b);
int subtract(int a, int b);
}
public interface AdvancedOperation {
int multiply(int a, int b);
int divide(int a, int b);
}
通过Proxy::getProxyClass
获取动态生成的代理类:
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?>[] interfaces = {BasicOperation.class, AdvancedOperation.class};
Class<?> proxyClass = Proxy.getProxyClass(classLoader, interfaces);
注意:这个proxyClass
只存在于JVM运行时,无法直接查看其成员。
2.2 导出$Proxy类文件
要深入分析$Proxy
类,需要将其导出到磁盘:
Java 8方式:
-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
或通过代码设置:
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
Java 9+方式:
-Djdk.proxy.ProxyGenerator.saveGeneratedFiles=true
为何有差异?因为Java模块系统导致
ProxyGenerator
类从sun.misc
包迁移到了java.lang.reflect
包(JDK-8145416)
关键点:
ProxyGenerator
类只读取一次该属性System.setProperty
在JVM生成代理类后无效- 生成代理类的操作包括:
- 显式调用:
Proxy::getProxyClass
或Proxy::newProxyInstance
- 隐式调用:读取注解时(尤其在单元测试框架中)
- 显式调用:
导出位置与类名直接相关,例如类名com.sun.proxy.$Proxy0
会导出到:
当前目录/com/sun/proxy/$Proxy0.class
2.3 $Proxy类成员分析
查看生成的$Proxy0
类结构:
继承关系:
public final class $Proxy0 extends Proxy implements BasicOperation, AdvancedOperation
- 继承
Proxy
类 → 解释了动态代理只能代理接口 - 实现我们定义的两个接口
核心字段(已重命名增强可读性):
private static Method hashCodeMethod; // Object类方法
private static Method equalsMethod; // Object类方法
private static Method toStringMethod; // Object类方法
private static Method addMethod; // BasicOperation接口
private static Method subtractMethod; // BasicOperation接口
private static Method multiplyMethod; // AdvancedOperation接口
private static Method divideMethod; // AdvancedOperation接口
方法实现特点:
- 所有方法都委托给
InvocationHandler::invoke
- 通过构造函数获取
InvocationHandler
实例: ```java 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. 代理机制工作原理
深入分析`$Proxy`类的生成和加载过程,核心类是`Proxy`和`ProxyGenerator`。
> 不同Java版本实现差异较大,我们重点分析LTS版本:Java 8/11/17
### 3.1 Java 8实现流程
生成过程分五步:

1. 入口方法:`Proxy::getProxyClass`或`Proxy::newProxyInstance`
2. 调用`Proxy::getProxyClass0`(私有方法)
3. 调用`ProxyClassFactory::apply`(静态嵌套类)
- 确定包名、类名、访问标志
4. 调用`ProxyGenerator::generateProxyClass`(`sun.misc`包的public类)
- 创建`ProxyGenerator`实例
- 调用`generateClassFile`生成字节码
- 可选导出class文件
- 返回字节数组
5. 调用`Proxy::defineClass0`加载字节码到JVM
### 3.2 Java 11改进点
相比Java 8有三项重大变化:

1. 新增`Proxy::getProxyConstructor`方法和`ProxyBuilder`嵌套类
2. 适应模块系统:
- `ProxyGenerator`迁移到`java.lang.reflect`包
- 变为包私有类
3. 使用`Unsafe::defineClass`加载字节码
### 3.3 Java 17优化
相比Java 11有两项关键改进:

1. 字节码生成:
- `ProxyGenerator`改用JDK内置ASM框架
2. 类加载:
- 使用`JavaLangAccess::defineClass`加载字节码
## 4. 注解中的代理应用
Java注解本质是特殊接口类型。但如何创建注解实例?**实际上不需要手动创建**——当使用反射API读取注解时,JVM会动态生成`$Proxy`类作为注解类型的实现:
```java
FunctionalInterface instance = Consumer.class.getDeclaredAnnotation(FunctionalInterface.class);
Class<?> clazz = instance.getClass();
boolean isProxyClass = Proxy.isProxyClass(clazz);
assertTrue(isProxyClass);
验证逻辑:
- 获取
Consumer
类的FunctionalInterface
注解实例 - 获取实例的Class对象
- 用
Proxy::isProxyClass
验证是否为代理类
5. 总结
本文通过以下方式深入剖析了$Proxy
类:
- 通过动态代理示例演示基本用法
- 导出并分析
$Proxy
类的成员结构 - 对比不同Java版本中代理生成和加载机制
- 揭示注解实现与代理机制的关系
核心结论:
$Proxy
类是动态代理的核心载体- 不同Java版本实现差异显著(Java 8→11→17)
- 注解机制底层同样依赖动态代理
完整示例代码见GitHub仓库