JDK 动态代理源码实现原理

461 阅读4分钟

JDK 动态代理

JDK 动态代理的核心是 InvocationHandler 接口。这里有个demo

public class DemoInvokerHandler implements InvocationHandler {

    private Object target; // 真正的业务对象,也就是RealSubject对象

    // DemoInvokerHandler构造方法
    public DemoInvokerHandler(Object target) { 
        this.target = target;
    }

  
    public Object invoke(Object proxy, Method method, Object[] args)
             throws Throwable {
        ... // 在执行业务逻辑之前的预处理逻辑
        Object result = method.invoke(target, args);
        ... // 在执行业务逻辑之后的后置处理逻辑
        return result;
    }

    public Object getProxy() {
        // 创建代理对象
        return Proxy.newProxyInstance(Thread.currentThread()
            .getContextClassLoader(),
                target.getClass().getInterfaces(), this);
    }
}

接下来,创建一个 main() 方法来模拟使用方创建并使用 DemoInvokerHandler 动态生成代理对象,示例代码如下:

public class Main {

    public static void main(String[] args) {

        Subject subject = new RealSubject();
        DemoInvokerHandler invokerHandler = new DemoInvokerHandler(subject);

        // 获取代理对象
        Subject proxy = (Subject) invokerHandler.getProxy();
        // 调用代理对象的方法,它会调用DemoInvokerHandler.invoke()方法
        proxy.operation();
    }

}

现在假设有多个业务逻辑类,需要相同的预处理逻辑和后置处理逻辑,那么只需要提供一个 InvocationHandler 接口实现类即可。在程序运行过程中,JDK 动态代理会为每个业务类动态生成相应的代理类实现,并加载到 JVM 中,然后创建对应的代理实例对象。

下面我们就接着来深入分析一下 JDK 动态代理底层动态创建代理类的原理。

首先,从前面的示例代码中可以看出,JDK 动态代理的入口方法是 Proxy.newProxyInstance(),这个静态方法有以下三个参数。

  • loader(ClassLoader 类型):加载动态生成的代理类的类加载器。
  • interfaces(Class[] 类型):业务类实现的接口。
  • h(InvocationHandler 类型):自定义的 InvocationHandler 对象。

下面进入 Proxy.newProxyInstance() 方法,查看其具体实现如下:

public static Object newProxyInstance(ClassLoader loader,Class[] interfaces, InvocationHandler h) 
    throws IllegalArgumentException {
    
    final Class<?>[] intfs = interfaces.clone();
    ... // 省略权限检查等代码
    Class<?> cl = getProxyClass0(loader, intfs);  // 获取代理类
    ... // 省略try/catch代码块和相关异常处理
    // 获取代理类的构造方法
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    final InvocationHandler ih = h;
    return cons.newInstance(new Object[]{h});  // 创建代理对象

}

从 newProxyInstance() 方法的具体实现代码中我们可以看到,JDK 动态代理是在 getProxyClass0() 方法中完成代理类的生成和加载。getProxyClass0() 方法的具体实现如下:

//proxyClassCache 是定义在 Proxy 类中一个静态字段,它是 WeakCache 类型的集合,用于缓存已经创建过的代理类。
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache
     = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

private static Class getProxyClass0 (ClassLoader loader, Class... interfaces) {

    // 边界检查,限制接口数量(略)
    // 如果指定的类加载器中已经创建了实现指定接口的代理类,则查找缓存;
    // 否则通过ProxyClassFactory创建实现指定接口的代理类
    return proxyClassCache.get(loader, interfaces);
}

WeakCache.get() 方法会首先尝试从缓存中查找代理类,如果查找失败,则会创建相应的 Factory 对象并调用其 get() 方法获取代理类。Factory 是 WeakCache 中的内部类,在 Factory.get() 方法中会通过 ProxyClassFactory.apply() 方法创建并加载代理类。

在 ProxyClassFactory.apply() 方法中,首先会检测代理类需要实现的接口集合,然后确定代理类的名称,之后创建代理类并将其写入文件中,最后加载代理类,返回对应的 Class 对象用于后续的实例化代理类对象。该方法的具体实现如下:

public Class apply(ClassLoader loader, Class[] interfaces) {

    // ... 对interfaces集合进行一系列检测(略)
    // ... 选择定义代理类的包名(略)
    // 代理类的名称是通过包名、代理类名称前缀以及编号这三项组成的
    long num = nextUniqueNumber.getAndIncrement();
    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    // 生成代理类,并写入文件
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
    // 加载代理类,并返回Class对象
    return defineClass0(loader, proxyName, proxyClassFile, 0, 
      proxyClassFile.length);
}

ProxyGenerator.generateProxyClass() 方法会按照指定的名称和接口集合生成代理类的字节码,并根据条件决定是否保存到磁盘上。该方法的具体代码如下:

public static byte[] generateProxyClass(final String name,
       Class[] interfaces) {

    ProxyGenerator gen = new ProxyGenerator(name, interfaces);
    // 动态生成代理类的字节码,具体生成过程不再详细介绍
    final byte[] classFile = gen.generateClassFile();
    // 如果saveGeneratedFiles值为true,会将生成的代理类的字节码保存到文件中
    if (saveGeneratedFiles) { 
        java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction() {
                public Void run() {
                    // 省略try/catch代码块
                    FileOutputStream file = new FileOutputStream(
                        dotToSlash(name) + ".class");
                    file.write(classFile);
                    file.close();
                    return null;
                }
            }
        );
    }

    return classFile; // 返回上面生成的代理类的字节码

}

最后,为了清晰地看到 JDK 动态生成的代理类的真正代码,我们需要将上述生成的代理类的字节码进行反编译。上述示例为 RealSubject 生成的代理类,反编译后得到的代码如下:

public final class $Proxy143 
      extends Proxy implements Subject {  // 实现了Subject接口
    // 这里省略了从Object类继承下来的相关方法和属性
    private static Method m3;
    static {
        // 省略了try/catch代码块
        // 记录了operation()方法对应的Method对象
        m3 = Class.forName("design.proxy.Subject")
          .getMethod("operation", new Class[0]);
    }
    // 构造方法的参数就是我们在示例中使用的DemoInvokerHandler对象
    public $Proxy11(InvocationHandler var1) throws {
        super(var1); 
    }

    public final void operation() throws {
        // 省略了try/catch代码块
        // 调用DemoInvokerHandler对象的invoke()方法
        // 最终调用RealSubject对象的对应方法
        super.h.invoke(this, m3, (Object[]) null);
    }
}

到此为止,JDK 动态代理的基本使用以及核心原理就分析完了。

总结

JDK 动态代理的实现原理是:动态创建代理类,然后通过指定类加载器进行加载。在创建代理对象时,需要将 InvocationHandler 对象作为构造参数传入;当调用代理对象时,会调用 InvocationHandler.invoke() 方法,从而执行代理逻辑,最终调用真正业务对象的相应方法。