> 文章列表 > 【源码解析】JDK 动态代理实现

【源码解析】JDK 动态代理实现

【源码解析】JDK 动态代理实现

基本流程

方法

在该方法中,我们呈现了在日常使用 JDK 动态代理机制的方法。

public class VehicleDynamicProxy {  /**  * 被代理对象  */  public Vehicle targetVehicle;  public VehicleDynamicProxy(Vehicle targetVehicle) {  this.targetVehicle = targetVehicle;  }  public Vehicle getProxy() {  //获取类的加载器  ClassLoader classLoader = targetVehicle.getClass().getClassLoader();  //获取类的接口数组  Class<?>[] interfaces = targetVehicle.getClass().getInterfaces();  //调用函数,用来的调用方法  InvocationHandler invocationHandler = new InvocationHandler() {  @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  Object result = null;  try {  System.out.println("前置通知 before");  //调用被代理对象的方法  result = method.invoke(targetVehicle, args);  System.out.println("返回通知 afterReturning");  } catch (Exception e) {  //异常通知 AfterThrowing                    throw new RuntimeException(e);  } finally {  //最终通知 After                }  return result;  }  };  //此时的proxy对象是实现了Vehicle接口,继承了Proxy类的代理对象  Vehicle proxy = (Vehicle) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);  return proxy;  }  
}

解读:

  • 我们应该特别注意到 Proxy.newProxyInstance() 方法,该方法就是我们关注的生成代理对象的方法;
  • 接下来,我们进入到该方法的内部去观察;

Proxy.newProxyInstance ()

为突出方法重点,我们只保留了核心逻辑,省略了异常捕获和其他校验的代码。

public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException  
{  Objects.requireNonNull(h);  final Class<?>[] intfs = interfaces.clone();  final SecurityManager sm = System.getSecurityManager();  if (sm != null) {  checkProxyAccess(Reflection.getCallerClass(), loader, intfs);  }  Class<?> cl = getProxyClass0(loader, intfs);  final Constructor<?> cons = cl.getConstructor(constructorParams);  return cons.newInstance(new Object[]{h});  
}

解读:

  • 我们先校验了相关权限,然后通过 getProxyClass0(loader, intfs); 生成代理类的 Class 对象(此时,我们可以通过这个对象生成代理类实例),最后我们通过构造器的方式生成代理类实例;
  • 毫无疑问,getProxyClass0 方法是最重要的。这也是我们探索的主要阵地;

getProxyClass0 ()

private static Class<?> getProxyClass0(ClassLoader loader,  Class<?>... interfaces) {  if (interfaces.length > 65535) {  throw new IllegalArgumentException("interface limit exceeded");  }  // If the proxy class defined by the given loader implementing  // the given interfaces exists, this will simply return the cached copy;    // otherwise, it will create the proxy class via the ProxyClassFactory    return proxyClassCache.get(loader, interfaces);  
}

解读:

  • 从源码注释我们就可以知道,倘若类加载器和接口数组都已经存在,那么就会通过 ProxyClassFactory 创建一个代理类的 Class 对象;
  • 很明显,我们应该看 ProxyClassFactory 类;

ProxyClassFactory

该类是 Proxy 类的一个静态内部工厂类。如下图所示:
【源码解析】JDK 动态代理实现
我们还可以发现该类只有一个方法apply(),所以只有这个方法才可以生成我们的代理类:
【源码解析】JDK 动态代理实现

接下来是对于ProxyClassFactory.apply()的解读,同样,我们会精简出核心的逻辑:

@Override  public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {  Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);  // package to define proxy class inString proxyPkg = null;       int accessFlags = Modifier.PUBLIC | Modifier.FINAL;  if (proxyPkg == null) {  // if no non-public proxy interfaces, use com.sun.proxy package  proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";  }  long num = nextUniqueNumber.getAndIncrement();  String proxyName = proxyPkg + proxyClassNamePrefix + num;  /*  * Generate the specified proxy class.         */         */byte[] proxyClassFile = ProxyGenerator.generateProxyClass(  proxyName, interfaces, accessFlags);  return defineClass0(loader, proxyName,  proxyClassFile, 0, proxyClassFile.length);  }  
}

解读:

  • 首先我们先生成一个代理类的名字 proxyNameproxyClassNamePrefix 的值就为我们喜闻乐见的:
    【源码解析】JDK 动态代理实现

    同时也为我们指示了包名:
    【源码解析】JDK 动态代理实现

  • 然后,我们通过 generateProxyClass 方法生成了代理类的字节码,可以看到这里特别点名要使用到我们提供的接口数组

  • 最后,调用本地方法 defineClass0 生成 Class 对象:

  • 显然,我们应该继续追 generateProxyClass 方法的源码;

ProxyGenerator. generateProxyClass ()

参数描述:

  • var0:代理类的名字;
  • var1:接口数组;
  • var2:权限校验参数(不用管);
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {  ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);  final byte[] var4 = var3.generateClassFile();  if (saveGeneratedFiles) {  AccessController.doPrivileged(new PrivilegedAction<Void>() {  public Void run() {  try {  int var1 = var0.lastIndexOf(46);  Path var2;  if (var1 > 0) {  Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));  Files.createDirectories(var3);  var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");  } else {  var2 = Paths.get(var0 + ".class");  }  Files.write(var2, var4, new OpenOption[0]);  return null;                } catch (IOException var4x) {  throw new InternalError("I/O exception saving generated file: " + var4x);  }  }  });  }  return var4;  
}

解读:

  • 其实该方法只用看前两行。通过 var3.generateClassFile() 生成一个 byte 数组 var4,该数组就是代理类对应的 Class 对象的字节表示,该数组会通过本地方法 defineClass0 生成 Class 对象;
  • saveGeneratedFiles 属性主要是决定程序是否将生成的动态代理对象保存到磁盘上,这个属性将在我们剖析生成的动态代理类结构发挥极大的作用;

总结

通过上述对于源码的解析,我们可以发现,动态代理对象的创建涉及到底层本地方法,也就是说,动态代理对象是通过我们提供的类信息由 JVM 虚拟机自动创建的。这就是动态代理区别于其他代理方式的根本不同。
动态代理方式提供了更加的灵活的选择。


不过,我们还没有解决为什么在生成代理对象的时候要给出接口的问题。
我们将通过解析运行阶段生成的代理对象来分析该问题。

代理对象详解

调试理解

我们先通过调试来看看这个对象究竟是什么:
【源码解析】JDK 动态代理实现
解读:

  • 我们的动态代理类原来叫做 $Proxy0,我们接下来直接通过一些手段拿到该动态代理类的字节码,然后进行反编译;

拿到动态代理对象

注意到,ProxyGenerator.generateProxyClass() 方式中的 saveGeneratedFiles 参数,我们通过这个参数来让动态代理对象写入到磁盘中。
有两种方法将该参数调整为 true。
设计 vm 参数:

-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true

在生成代理对象前调用如下代码:

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

于是,我们就可以在项目主目录下,拿到 JVM 自动生成的动态代理类:
【源码解析】JDK 动态代理实现

代理对象剖析

反编译后如下,我们在该类中省略了无关的方法实现,例如 hashcodetoString,只保留了被代理对象实现的方法:

package com.sun.proxy;import com.yelanyanyu.dynamicProxy.Vehicle;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0 extends Proxy implements Vehicle {private static Method m3;private static Method m4;public $Proxy0(InvocationHandler var1) throws  {super(var1);}public final void run() throws  {try {super.h.invoke(this, m3, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}public final String fly(int var1) throws  {try {return (String)super.h.invoke(this, m4, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}static {try {m3 = Class.forName("com.yelanyanyu.dynamicProxy.Vehicle").getMethod("run");m4 = Class.forName("com.yelanyanyu.dynamicProxy.Vehicle").getMethod("fly", Integer.TYPE);} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}

解读:

  • 从方法的描述 $Proxy0 extends Proxy implements Vehicle 可以看出,该代理类的类名就是我们之前分析的 $Proxy0
  • 该类继承原来的 Proxy 类,实现我们提供的接口;
  • 特别注意return (String)super.h.invoke(this, m4, new Object[]{var1});,我们可以再次印证我在前一篇文章中得出的结论,代理对象的方法调用是通过转发到 InvocationHandler 的 invoke 来实现的;

为什么一定是接口

现在我们就可以来解释解释这个问题了。原因主要有:

  • 代理对象是动态创建的,也就是说,程序员刚开始并不知道其类名,也不能在编译阶段使用这个代理对象。所以,我们只能使用 OOP 原则中的向下转型,用 $Proxy0 的父类或者其实现的接口来接收,但是 $Proxy0 的父类已经是 Proxy 类了(Proxy 类中没有对应的方法);由于 Java 是单继承的,所以只能用实现接口的方式,来让接口接受 JVM 创建的动态代理对象,如下:
Vehicle proxy = (Vehicle) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

解读:

  • 我们不可能写成 $Proxy0 proxy = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

  • 或者 Proxy proxy = (Proxy) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

  • 另一个原因则是默认了对象采用了合适的模板设计模式:类的绝大部分方法都在接口中有实现,代理对象可以使用到被代理对象的绝大部分方法。