【1.JDK底层源码剖析】6.1 Proxy动态代理生成和执行原理剖析

你想了解 Java 11 中 Proxy 动态代理的生成和执行原理,并结合底层源码进行详细说明。我会尽力为你剖析其核心机制。由于动态代理的底层源码涉及 Native 方法且代码非常复杂,我会重点阐述其核心流程和关键设计,并提供一些查看源码的建议。

⚙️ Proxy 动态代理的核心机制

Java 动态代理主要涉及 java.lang.reflect.Proxyjava.lang.reflect.InvocationHandler

  • Proxy:提供静态方法(如 newProxyInstance)创建动态代理类和实例。
  • InvocationHandler:代理实例的方法调用会被分派到其调用处理器的 invoke 方法。

🔧 代理类的生成过程

核心入口是 Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

1. 获取或生成代理类 Class 对象

Proxy.getProxyClass0(loader, interfaces) 负责获取或生成代理类的 Class 对象。它使用了一个 WeakCache (proxyClassCache) 进行缓存,确保对同一组接口和类加载器,只会生成一个代理类。

// Proxy.java 片段
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
   
   
    // ... 参数验证 (如接口数量上限检查)
    return proxyClassCache.get(loader, interfaces); // 从缓存中获取或生成
}

如果缓存未命中,则会通过 ProxyClassFactory (作为 WeakCacheValueFactory) 来生成代理类的字节码并定义类。

2. ProxyClassFactory 生成字节码

ProxyClassFactory.apply 方法是生成代理类的核心,其主要步骤包括:

  • 验证接口:确保所有 interfaces 都是接口且非重复,类加载器能访问它们。
  • 确定包名和访问权限
    • 如果所有接口都是 public,代理类将在 com.sun.proxy 包下。
    • 如果存在非 public 接口,代理类将和该接口在同一包下
    • 代理类的修饰符通常是 public final
  • 生成代理类名:名称格式为 com.sun.proxy.$Proxy + 唯一数字(原子递增)。
  • 生成字节码:调用 ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags) 生成代理类的字节码 (byte[])。ProxyGeneratorsun.misc 包下的类,它会遍历所有接口的所有方法,生成对应的字节码。
  • 定义代理类:调用 defineClass0 这个 Native 方法,将字节码数组 (byte[]) 转换为 Class<?> 对象。这个方法最终会调用 ClassLoaderdefineClass 方法。
3. 生成代理实例

获取到代理类的 Class 对象后,newProxyInstance 方法会通过反射获取其参数类型为 InvocationHandler 的构造器:

// Proxy.java 片段
final Constructor<?> cons = cl.getConstructor(constructorParams); // constructorParams 为 new Class<?>[]{InvocationHandler.class}

然后,传入我们提供的 InvocationHandler 实例 h,通过 cons.newInstance(new Object[]{h}) 创建代理实例。这意味着生成的代理类都会有一个接收 InvocationHandler 参数的构造器

📝 生成的代理类结构(概念模型)

通过设置系统属性 sun.misc.ProxyGenerator.saveGeneratedFilestrue,可以将生成的代理类字节码文件(.class)保存到磁盘,然后使用反编译工具查看。一个生成的代理类大致结构如下:

// 反编译后的 $ProxyN.class 概念模型
public final class $Proxy0 extends Proxy implements TargetInterface1, TargetInterface2 {
   
    // 继承 Proxy 并实现你指定的接口

    // 静态字段:缓存 Method 对象
    private static Method m1; // hashCode()
    private static Method m2; // equals()
    private static Method m3; // toString()
    private static Method m4; // 你的接口方法1
    private static Method m5; // 你的接口方法2

    // 静态初始化块:类加载时通过反射获取 Method 对象
    static {
   
   
        try {
   
   
            m1 = Class.forName("java.lang.Object").getMethod("hashCode");
            m2 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.yourpackage.TargetInterface1").getMethod("methodName1", ParameterTypes...);
            m5 = Class.forName("com.yourpackage.TargetInterface2").getMethod("methodName2", ParameterTypes...);
            // ... 其他方法
        } catch (NoSuchMethodException | ClassNotFoundException e) {
   
   
            throw new NoSuchMethodError(e.getMessage())
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值