动态代理类在运行时由JVM动态生成并加载,字节码由ProxyGenerator实时计算生成(如$Proxy0),经defineClass加载至方法区,再完成验证、准备、解析、初始化等标准类加载流程。

动态代理生成的类是在运行时由 JVM 动态加载的,整个过程不依赖预先存在的 .class 文件,而是通过字节码生成、内存加载、验证链接初始化等标准类加载流程完成的。
动态代理类的字节码是运行时计算生成的
Java 的 java.lang.reflect.Proxy 类在调用 newProxyInstance() 时,会根据传入的接口列表,调用 ProxyGenerator.generateProxyClass() 方法,在内存中生成符合 JVM 规范的 class 字节码(二进制流)。这个字节码内容不是来自磁盘或网络,而是由 Java 反射机制实时“算出来”的,形式通常是 $Proxy0、$Proxy1 这样的类名。
- 生成逻辑封装在 JDK 内部,开发者不可见但可被替换(如 CGLIB 就绕过接口限制,直接操作字节码)
- 生成的字节码严格遵循 JVM Class 文件格式,包含常量池、字段表、方法表、属性表等结构
- 该字节码未落地为磁盘文件,只存在于内存中,属于“运行时计算生成”的典型场景
类加载器负责将字节码加载进方法区
生成字节码后,JVM 会使用当前线程上下文类加载器(Context ClassLoader)或代理目标类所用的类加载器,调用 defineClass() 方法,把字节码流注册为一个合法的类定义。
-
defineClass()是ClassLoader的受保护方法,它把原始字节流解析成 JVM 可识别的内部数据结构,并在方法区(HotSpot 中为元空间)创建对应的运行时类数据 - 同时在堆中创建一个
java.lang.Class实例,作为该代理类的唯一标识和访问入口 - 这个过程发生在“加载阶段”,属于类加载五步(加载→验证→准备→解析→初始化)的第一步
后续阶段按标准流程执行,无特殊跳过
字节码加载完成后,JVM 立即进入链接与初始化阶段,和其他普通类完全一致:
立即学习“Java免费学习笔记(深入)”;
- 验证:检查代理类字节码是否合规(比如方法签名是否匹配接口、指令是否越界),确保不破坏 JVM 安全模型
-
准备:为代理类的静态变量分配内存并设默认值(如
static final long serialVersionUID) - 解析:将符号引用(如接口方法名、字段描述符)转为直接内存地址引用
-
初始化:执行
<clinit>方法——对代理类来说,通常为空,因为没显式静态代码块;但若生成逻辑中注入了静态初始化逻辑,也会在此执行
加载时机由首次使用触发,且具备类加载器可见性约束
代理类不会在生成字节码后立刻加载,而是在第一次被主动使用时才真正触发加载流程,例如:
- 首次通过
Proxy.newProxyInstance()创建代理实例 - 反射调用该代理类的静态方法(极少发生)
- 通过
Class.forName("com.sun.proxy.$Proxy0")显式加载(不推荐,名称不稳定)
需要注意:代理类只能被其生成时所用的类加载器及其子加载器看到;若父加载器尝试加载,会因双亲委派机制失败而抛出 ClassNotFoundException——这是动态代理跨类加载器场景下常见问题的根源。


















