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::getProxyClassProxy::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实现流程

生成过程分五步:
![Java 8流程图](/wp-content/uploads/2022/05/p8.png)

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有三项重大变化:
![Java 11流程图](/wp-content/uploads/2022/05/p11.png)

1. 新增`Proxy::getProxyConstructor`方法和`ProxyBuilder`嵌套类
2. 适应模块系统:
   - `ProxyGenerator`迁移到`java.lang.reflect`包
   - 变为包私有类
3. 使用`Unsafe::defineClass`加载字节码

### 3.3 Java 17优化

相比Java 11有两项关键改进:
![Java 17流程图](/wp-content/uploads/2022/05/p17.png)

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);

验证逻辑:

  1. 获取Consumer类的FunctionalInterface注解实例
  2. 获取实例的Class对象
  3. Proxy::isProxyClass验证是否为代理类

5. 总结

本文通过以下方式深入剖析了$Proxy类:

  1. 通过动态代理示例演示基本用法
  2. 导出并分析$Proxy类的成员结构
  3. 对比不同Java版本中代理生成和加载机制
  4. 揭示注解实现与代理机制的关系

核心结论:

  • $Proxy类是动态代理的核心载体
  • 不同Java版本实现差异显著(Java 8→11→17)
  • 注解机制底层同样依赖动态代理

完整示例代码见GitHub仓库


原始标题:What Is the JDK com.sun.proxy.$Proxy Class?