Java
语言基础

动态代理原理详解(1) - JDK实现

简介:代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

1. 代理模式介绍

代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。

注:代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

举个例子来说明代理的作用:一般作为老板都会雇佣一个秘书,在处理日常事务的时候,秘书可以代为老板处理大部分琐碎的业务(如接待客人,通知公司决策),而处理核心业务(如签字,审批财务等)时秘书需要通过老板的授权才可以处理。在这个例子中,老板就是一个目标对象,他只要负责核心业务的授权,而其他琐碎的事情就交给他的秘书来解决。

代理模式包含如下角色:

  • SubjectInterface:抽象接口,是一个接口。该接口是对象和它的代理共用的接口。
  • RealSubject:目标类,是实现抽象接口的类。
  • Proxy:代理类,内部含有对目标类RealSubject对象的引用,从而可以操作目标类对象。代理对象提供与目标类对象相同的接口,以便在任何时刻都能代替目标类对象。同时,代理对象可以在执行目标类对象操作时,附加其他的操作,相当于对目标类对象进行封装。

在Java中,代理的实现一般有三种形式,下面将分别以一个简单的示例进行介绍。

2. 静态代理

生产跑车的厂商在跑车完成装配之后,都会进行汽车的速度测试,厂商可以将这些测试工作交由一个代理商来完成。下面我们就使用动态代理模拟这个过程。

首先我们需要定义一个跑车的接口类(即目标类接口),就叫做Car,代码如下:

  • public interface Car {
  • // 返回汽车相关信息
  • String getInfo();
  • // 汽车行驶方法
  • void run();
  • }

上面的接口很简单,只限定了两个接口,一个用于获取汽车相关的信息,一个是汽车行驶方法的顶层标准接口。

接下来分别设计两辆车(即目标类):

  • public class Bugatti implements Car {
  • private String info;
  • public Bugatti(String info) {
  • this.info = info;
  • }
  • @Override
  • public void run() {
  • System.out.println(this.info + " car running");
  • try {
  • // 模拟行驶耗时
  • Thread.sleep(new Random().nextInt(3000));
  • } catch (Exception e) {
  • e.printStackTrace();
  • }
  • }
  • @Override
  • public String getInfo() {
  • return this.info;
  • }
  • }
  • public class Ferrari implements Car {
  • private String info;
  • public Ferrari(String info) {
  • this.info = info;
  • }
  • @Override
  • public void run() {
  • System.out.println(this.info + " car running");
  • try {
  • // 模拟行驶耗时
  • Thread.sleep(new Random().nextInt(3000));
  • } catch (Exception e) {
  • e.printStackTrace();
  • }
  • }
  • @Override
  • public String getInfo() {
  • return this.info;
  • }
  • }

在上面的代码中分别设计了布加迪和法拉利两辆跑车,代码结构基本类似,唯一不同的地方在于run()方法的实现中两辆车的行驶耗时进行随机程度的模拟。

既然要测试跑车的速度,必然是要进行计时操作的,这里我们将计时操作设计为一个工具类,代码如下:

  • public class SpeedTestUtil {
  • // 存储启动时间
  • private static ThreadLocal<Long> startTime = new ThreadLocal<Long>();
  • public static void start() {
  • // 记录启动时间
  • startTime.set(System.currentTimeMillis());
  • }
  • public static void finish(String carName, String methodName) {
  • // 获取结束时间
  • long finishTime = System.currentTimeMillis();
  • // 打印时间信息
  • System.out.println(carName + " 执行" + methodName + ",耗时:" + (finishTime - startTime.get()) + " ms.\n");
  • }
  • }

测试工具类的功能相对简单,但用到了ThreadLocal避免线程问题。该工具类主要用于记录执行开始和结束的时间并进行耗时计算。

接下来需要定义最重要的代理类了,这里我们命名为SpeedTestProxy,它主要用于对各类型号的车进行速度测试,代码如下:

  • public class SpeedTestProxy implements Car {
  • Car targetCar;
  • public SpeedTestProxy(Car targetCar) {
  • this.targetCar = targetCar;
  • }
  • @Override
  • public String getInfo() {
  • if (this.targetCar != null){
  • return this.targetCar.getInfo();
  • } else {
  • return "";
  • }
  • }
  • @Override
  • public void run() {
  • if (this.targetCar != null) {
  • System.out.println("静态测速代理开始测试车型:" + this.targetCar.getInfo());
  • SpeedTestUtil.start();
  • targetCar.run();
  • SpeedTestUtil.finish(this.targetCar.getInfo(), "run");
  • } else {
  • System.out.println("You need provide a Car to test");
  • }
  • }
  • }

代码也非常简单,SpeedTestProxy的构造方法要求传入需要进行测试的车,然后run()方法中进行主要的测试过程。

上面的代码实现了代理模式中所有的关键对象,下面就开始进行Bugatti和Ferrari两辆车的速度测试:

  • private static void testSpeed() {
  • // 测试Bugatti
  • Bugatti bugatti = new Bugatti("Bugatti Chiron");
  • SpeedTestProxy bugattiTest = new SpeedTestProxy(bugatti);
  • bugattiTest.run();
  • // 测试Ferrari
  • Ferrari ferrari = new Ferrari("Ferrari California T");
  • SpeedTestProxy ferrariTest = new SpeedTestProxy(ferrari);
  • ferrariTest.run();
  • }

测试过程的主要代码也非常简单,只需要先生产两辆不同型号的车,然后扔给测试代理,测试代理调用自己的test()方法,即可得到相应的测试结果,上述代码运行后会得到打印如下信息:

  • 静态测速代理开始测试车型:Bugatti Chiron
  • Bugatti Chiron car running
  • Bugatti Chiron 执行run,耗时:2125 ms.
  • 静态测速代理开始测试车型:Ferrari California T
  • Ferrari California T car running
  • Ferrari California T 执行run,耗时:2566 ms.

在上面的例子中,通过SpeedTestProxy类以静态代理的方式成功为Bugatti和Ferrari两个类在run()方法上扩展了速度测试的功能,虽然可以做到在不修改目标类的功能前提下进行功能扩展,但如果想要让SpeedTestProxy类实例在包装目标类实例(即Bugatti类和Ferrari类实例)后屏蔽SpeedTestProxy类的内部细节,保持与目标类的功能相同,则需要让SpeedTestProxy类与目标类实现一样的接口,这种情况下一旦接口增加方法,目标对象与代理对象都要维护。

3. 动态代理(JDK实现)

动态代理解决了静态代理存在的部分问题。动态代理通过Java反射的技术,动态地在底层新构造一个动态类,实现对目标类的功能的封装。动态代理可以做到只需要对目标类的某些功能进行增强,而无需关注目标类中其他不需要的功能。动态代理有以下特点:

  1. 代理对象不需要实现接口。
  2. 代理对象的生成是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象 / 目标对象实现的接口的类型)。
  3. 动态代理也叫做JDK代理、接口代理。

3.1. 动态代理的基本使用

接下来我们使用动态代理来完成上面的测试操作。我们首先直接实现动态代理的完整过程,然后对关键代码和类进行解释。与静态代理一样,在动态代理中我们需要有一个类对目标类的特定功能进行扩展,动态代理则要求实现扩展的这个类需要实现InvocationHandler接口,在本例中,该类代码如下:

  • public class SpeedTestInvocationHandler<T extends Car> implements InvocationHandler {
  • T targetCar;
  • public SpeedTestInvocationHandler(T targetCar) {
  • this.targetCar = targetCar;
  • }
  • @Override
  • public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  • if (targetCar != null) {
  • if ("run".equals(method.getName())) {
  • System.out.println("动态测速代理开始执行:" + this.targetCar.getInfo() + " " + method.getName());
  • SpeedTestUtil.start();
  • Object result = method.invoke(this.targetCar, args);
  • SpeedTestUtil.finish(this.targetCar.getInfo(), method.getName());
  • return result;
  • } else {
  • return method.invoke(this.targetCar, args);
  • }
  • } else {
  • throw new Throwable("Target Error, You need provide a Car to test");
  • }
  • }
  • }

上述代码中,SpeedTestInvocationHandler类的构造方法要求传入一个继承自Car接口的对象,同时该类实现了InvocationHandler接口中必须实现的方法invoke()方法,该方法主要用于处理目标类的各种方法的调用,在通过代理调用目标类的方法的时候,会统一交由该invoke()方法进行处理。在invoke()方法中,首先判断了targetCar对象是否为空,然后对方法名进行了判断,如果是调用的是run()方法,则添加测试功能,否则直接进行方法调用并返回结果即可。

有了InvocationHandler的实现类对特定功能进行扩展,就可以直接使用动态代理生成代理对象进行速度测试了,代码如下:

  • private static void testBugatti() throws Exception {
  • // 先创建需要测速的目标类
  • Bugatti bugatti = new Bugatti("Bugatti Chiron");
  • // 构造Bugatti类对象所关联的调用处理器
  • InvocationHandler handler = new SpeedTestInvocationHandler<Bugatti>(bugatti);
  • // 通过目标类的类加载器和目标类所实现的接口来获取一个代理类
  • Class<?> proxyClass = Proxy.getProxyClass(Bugatti.class.getClassLoader(), new Class[]{Car.class});
  • // 通过反射从代理类获取构造器,唯一参数类型是调用处理器接口类型
  • Constructor<?> constructor = proxyClass.getConstructor(new Class[]{InvocationHandler.class});
  • // 通过代理类构造器创建动态代理类实例,构造时调用处理器对象作为参数被传入
  • Car car = (Car) constructor.newInstance(new Object[]{handler});
  • car.run();
  • }

运行上述代码可以得到打印信息如下:

  • 动态测速代理开始执行:Bugatti Chiron run
  • Bugatti Chiron car running
  • Bugatti Chiron 执行run,耗时:2629 ms.

动态代理创建对象的一般步骤如下:

  1. 通过实现InvocationHandler接口自定义自己的调用处理器,在上面的例子中,SpeedTestInvocationHandler类则是一个典型的调用处理器,它拥有了目标类的引用targetCar

  2. 通过Proxy提供的getProxyClass()方法指定ClassLoader对象和一组interface来创建动态代理类,如上述例子中的:

  • Class<?> proxyClass = Proxy.getProxyClass(Bugatti.class.getClassLoader(), new Class[]{Car.class});
  1. 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型。如上述例子中的:
  • Constructor<?> constructor = proxyClass.getConstructor(new Class[]{InvocationHandler.class});
  1. 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。如上述例子中的:
  • Car car = (Car) constructor.newInstance(new Object[]{handler});

Java的Proxy提供了一个封装好的方法将上述步骤的2 ~ 4步进行了封装,只需要提供调用处理器即可:

  • private static void testFerrari() throws Exception {
  • final Ferrari ferrari = new Ferrari("Ferrari California T");
  • // 构造Ferrari类对象所关联的调用处理器
  • InvocationHandler handler = new SpeedTestInvocationHandler<Ferrari>(ferrari);
  • // 创建代理类对象
  • Car car = (Car) Proxy.newProxyInstance(Car.class.getClassLoader(), new Class[]{Car.class}, handler);
  • car.run();
  • }

一般来说,调用处理器需要拥有目标类对象的实例引用,用于invoke()方法派发的调用过程中,得益于Java匿名类的变量捕获特性,我们可以使用匿名的调用处理器直接对目标类进行处理,在这种情况下,目标类需要声明为final:

  • private static void testFerrari() throws Exception {
  • final Ferrari ferrari = new Ferrari("Ferrari California T");
  • // 匿名类形式的InvocationHandler调用处理器实现
  • Car car = (Car) Proxy.newProxyInstance(Car.class.getClassLoader(), new Class[]{Car.class}, new InvocationHandler() {
  • @Override
  • public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  • System.out.println("测速代理开始执行:" + ferrari.getInfo() + " " + method.getName());
  • SpeedTestUtil.start();
  • Object result = method.invoke(ferrari, args);
  • SpeedTestUtil.finish(ferrari.getInfo(), method.getName());
  • return result;
  • }
  • });
  • car.run();
  • }

上面就是我们经常看见的动态代理的调用形式了,它只是完整动态代理实现的一种简化,而两种方式内部的执行过程是一样的。

3.2. 重要类源码详解

  • java.lang.reflect.InvocationHandler:调用处理器接口,该接口只提供了一个invoke*()方法,用于实现该接口的类控制对于真正委托类的代理访问:
  • /**
  • * 该方法负责集中处理动态代理类上的所有方法调用,调用处理器根据传入的三个参数进行预处理或分派到委托类实例上进行执行
  • * @param proxy 代理类实例
  • * @param method 被调用的方法对象
  • * @param args 调用参数
  • * @return
  • * @throws Throwable
  • */
  • public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
  • java.lang.ClassLoader:类装载器类,将类的字节码装载到Java 虚拟机(JVM)中并为其定义类对象,然后该类才能被使用。Proxy类与普通类的唯一区别就是其字节码是由 JVM在运行时动态生成的而非预存在于任何一个.class文件中。每次生成动态代理类对象时都需要指定一个类装载器对象。

  • java.lang.reflect.Proxy:动态代理机制的主类,提供一组静态方法为一组接口动态的生成对象和代理类。重要的相关方法如下:

  • // 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
  • public static InvocationHandler getInvocationHandler(Object proxy) throws IllegalArgumentException;
  • // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
  • public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException;
  • // 方法 3:该方法用于判断指定类对象是否是一个动态代理类
  • public static boolean isProxyClass(Class<?> cl);
  • // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
  • public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException;

下面对该类的源代码进行详细分析:

  • package com.coderap.foundation.proxy;
  • import sun.misc.ProxyGenerator;
  • import java.lang.ref.Reference;
  • import java.lang.ref.WeakReference;
  • import java.lang.reflect.Constructor;
  • import java.lang.reflect.InvocationHandler;
  • import java.lang.reflect.InvocationTargetException;
  • import java.lang.reflect.Modifier;
  • import java.util.*;
  • public class Proxy implements java.io.Serializable {
  • private static final long serialVersionUID = -2222568056686623797L;
  • // 代理类名的前缀。生成的代理类一般是以 $Proxy + 数字 的形式命名的,如常见的 $Proxy0
  • private final static String proxyClassNamePrefix = "$Proxy";
  • // 代理类构造方法需要的参数类型
  • private final static Class[] constructorParams = {InvocationHandler.class};
  • /**
  • * 用于缓存代理类类加载器的Map,这个Map的格式如下:
  • * WeakHashMap - {
  • * ClassLoader:HashMap - {
  • * interfaceName : ProxyClass
  • * }
  • * }
  • */
  • private static Map loaderToCache = new WeakHashMap();
  • /**
  • * marks that a particular proxy class is currently being generated
  • * 用于标记代理类是否已经生成成功了
  • */
  • private static Object pendingGenerationMarker = new Object();
  • // 这个属性是用于构成代理类名称的,每生成一个代理类,该值就会+1,因此代理类的类名都是$Proxy0, $Proxy1, $Proxy2...依次递增的形式
  • private static long nextUniqueNumber = 0;
  • // 用作nextUniqueNumber递增过程的锁对象
  • private static Object nextUniqueNumberLock = new Object();
  • // 所以已生成的代理类,主要用于 {@link #isProxyClass} 方法的实现
  • private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());
  • // 用于引用传入的调用处理器
  • protected InvocationHandler h;
  • // 禁止外部构造
  • private Proxy() {
  • }
  • // 在构造时会保存传入的InvocationHandler以供后期使用
  • protected Proxy(InvocationHandler h) {
  • this.h = h;
  • }
  • /**
  • * 给定一个类加载器和一系列接口,返回一个代理类对象(java.lang.Class类型)
  • * 这个代理类对象会实现所有指定的接口,如果之前已经实现过相应接口序列的代理类,将会直接返回
  • * 否则根据类加载器动态生成一个新的代理类
  • *
  • * 对于interfaces参数有以下标准:
  • * 1. 必须都为接口,不能为类或其他基本类型;
  • * 2. 任意两个接口都不能引用同一Class对象(identical);
  • * 3. 所有的接口应该对传入的类加载器可见,也就是说对于任意接口i要满足 Class.forName(i.getName(), false, cl) == i ;
  • * 4. 所有的非public的接口应该在一个包中,否则代理类将无法实现这些接口,不管这个接口放在哪个包;
  • * 5. 对于有相同签名的指定接口中任何成员方法:
  • * - 如果任何方法的返回类型是基本类型或void,那么所有的方法必须具有与此相同的返回类型。
  • * - 否则,该方法之一必须是返回类型,它可以指派给该方法其余的所有返回类型。
  • * 6. 得到的代理类必须不超过虚拟机在类上施加的任何限制。例如,虚拟机可以限制某一类实现至多65535的接口数;在这种情况下,interfaces数组的大小必须不超过65535。
  • *
  • * 如果违反了这些限制,将抛出IllegalArgumentException。如果interfaces数组参数或其任何元素为null,则将抛出NullPointerException。
  • *
  • * 注意,指定的代理接口的顺序非常重要:对接口组合相同但顺序不同的代理类的两个请求会导致两个不同的代理类。
  • *
  • * @param loader 代理类的类加载器
  • * @param interfaces 代理类实现的接口集合
  • * @return 用指定的类加载器定义的代理类,它可以实现指定的接口
  • * @throws IllegalArgumentException 如果违反传递到getProxyClass的参数上的任何限制
  • * @throws NullPointerException 如果interfaces数组参数或其任何元素为null
  • */
  • public static Class<?> getProxyClass(ClassLoader loader,
  • Class<?>... interfaces)
  • throws IllegalArgumentException {
  • // 最多传入65535个接口
  • if (interfaces.length > 65535) {
  • throw new IllegalArgumentException("interface limit exceeded");
  • }
  • Class proxyClass = null;
  • // 从这里开始进行interfaces参数验证
  • String[] interfaceNames = new String[interfaces.length];
  • Set interfaceSet = new HashSet(); // for detecting duplicates
  • for (int i = 0; i < interfaces.length; i++) {
  • /*
  • * - 验证传入的接口对于传入的ClassLoader是否都为可见的(通过使用loader循环加载传入的interface,判断的到的interface与传入的interface是否一致);
  • */
  • String interfaceName = interfaces[i].getName();
  • Class interfaceClass = null;
  • try {
  • interfaceClass = Class.forName(interfaceName, false, loader);
  • } catch (ClassNotFoundException e) {
  • }
  • if (interfaceClass != interfaces[i]) {
  • throw new IllegalArgumentException(
  • interfaces[i] + " is not visible from class loader");
  • }
  • /*
  • * - 传入的接口是否是接口;
  • */
  • if (!interfaceClass.isInterface()) {
  • throw new IllegalArgumentException(
  • interfaceClass.getName() + " is not an interface");
  • }
  • /*
  • * - 验证是否重复加载(通过interfaceSet记录每个传入的接口)
  • */
  • if (interfaceSet.contains(interfaceClass)) {
  • throw new IllegalArgumentException(
  • "repeated interface: " + interfaceClass.getName());
  • }
  • interfaceSet.add(interfaceClass);
  • interfaceNames[i] = interfaceName;
  • }
  • Object key = Arrays.asList(interfaceNames);
  • // 对代理类进行缓存的判断
  • Map cache;
  • synchronized (loaderToCache) {
  • // 从loaderToCache这个缓存Map中根据类加载器读取,读取到的value还是一个Map
  • cache = (Map) loaderToCache.get(loader);
  • if (cache == null) {
  • // 如果读到的Map为空,则创建一个Map,并将其以类加载器为键存入loaderToCache中
  • cache = new HashMap();
  • loaderToCache.put(loader, cache);
  • }
  • }
  • /*
  • * Look up the list of interfaces in the proxy class cache using
  • * the key. This lookup will result in one of three possible
  • * kinds of values:
  • * null, if there is currently no proxy class for the list of
  • * interfaces in the class loader,
  • * the pendingGenerationMarker object, if a proxy class for the
  • * list of interfaces is currently being generated,
  • * or a weak reference to a Class object, if a proxy class for
  • * the list of interfaces has already been generated.
  • */
  • synchronized (cache) {
  • /*
  • * Note that we need not worry about reaping the cache for
  • * entries with cleared weak references because if a proxy class
  • * has been garbage collected, its class loader will have been
  • * garbage collected as well, so the entire cache will be reaped
  • * from the loaderToCache map.
  • */
  • do {
  • // 循环根据传入的interface接口从cache中读取
  • Object value = cache.get(key);
  • if (value instanceof Reference) {
  • // 如果是Reference,则在Reference中尝试获取代理类
  • proxyClass = (Class) ((Reference) value).get();
  • }
  • if (proxyClass != null) {
  • // proxy class already generated: return it
  • // 如果读到的不为空,表示之前已经进行了代理类的生成,则直接返回
  • return proxyClass;
  • } else if (value == pendingGenerationMarker) {
  • // 代理类正在被生成,需要等待
  • try {
  • cache.wait();
  • } catch (InterruptedException e) {
  • }
  • continue;
  • } else {
  • // 表示没有获取到相关的代理类,或者代理类正在生成,用这个属性进行标记
  • cache.put(key, pendingGenerationMarker);
  • break;
  • }
  • } while (true);
  • }
  • try {
  • String proxyPkg = null; // package to define proxy class in
  • /*
  • * 验证所有的非public的接口是否在同一个包,
  • * 如果不在,将会报错,如果都在同一个包,将记录这个包名
  • * 实现的代理类将放在这个包中
  • */
  • for (int i = 0; i < interfaces.length; i++) {
  • // 获取接口访问权限
  • int flags = interfaces[i].getModifiers();
  • if (!Modifier.isPublic(flags)) {
  • // 如果接口不是是public的
  • String name = interfaces[i].getName();
  • // 获取接口包名
  • int n = name.lastIndexOf('.');
  • String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
  • if (proxyPkg == null) {
  • proxyPkg = pkg;
  • } else if (!pkg.equals(proxyPkg)) {
  • throw new IllegalArgumentException(
  • "non-public interfaces from different packages");
  • }
  • }
  • }
  • if (proxyPkg == null) { // if no non-public proxy interfaces,
  • proxyPkg = ""; // use the unnamed package
  • }
  • {
  • // 维护代理标记数字
  • long num;
  • synchronized (nextUniqueNumberLock) {
  • num = nextUniqueNumber++;
  • }
  • // 代理类名即 包名 + proxyClassNamePrefix(本例中即 $Proxy) + 数字
  • String proxyName = proxyPkg + proxyClassNamePrefix + num;
  • // 生成指定的代理类文件
  • byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  • proxyName, interfaces);
  • try {
  • // 生成指定的代理类
  • proxyClass = defineClass0(loader, proxyName,
  • proxyClassFile, 0, proxyClassFile.length);
  • } catch (ClassFormatError e) {
  • throw new IllegalArgumentException(e.toString());
  • }
  • }
  • // 将代理类进行缓存
  • proxyClasses.put(proxyClass, null);
  • } finally {
  • /*
  • * 最后对生成的代理类进行缓存,缓存在cache(即loaderToCache)中,
  • * 以接口名为键,以WeakReference包装的代理类为值
  • */
  • synchronized (cache) {
  • if (proxyClass != null) {
  • cache.put(key, new WeakReference(proxyClass));
  • } else {
  • cache.remove(key);
  • }
  • cache.notifyAll();
  • }
  • }
  • return proxyClass;
  • }
  • /**
  • * 返回代理对象的实例对象,需要制定类加载器,接口列表和调用处理器
  • *
  • * @param h 调用处理器
  • * @param loader 类加载器
  • * @param interfaces 接口列表
  • * @return 代理类实例
  • * @throws IllegalArgumentException 如果违反传递到getProxyClass的参数上的任何限制(来自getProxyClass的异常)
  • * @throws NullPointerException 如果interfaces数组参数或其任何元素为null(来自getProxyClass的异常)
  • */
  • public static Object newProxyInstance(ClassLoader loader,
  • Class<?>[] interfaces,
  • InvocationHandler h)
  • throws IllegalArgumentException {
  • if (h == null) {
  • throw new NullPointerException();
  • }
  • // 根据传入的ClassLoader和interfaces查找对象的代理类
  • Class cl = getProxyClass(loader, interfaces);
  • try {
  • /**
  • * 获得代理类的构造器,这里默认传入的参数是constructorParams
  • * 所以我们在分步调用的时候可以直接传入new Class[]{InvocationHandler.class}
  • */
  • Constructor cons = cl.getConstructor(constructorParams);
  • // 反射方式创建代理对象,传入了调用处理器
  • return (Object) cons.newInstance(new Object[]{h});
  • } catch (NoSuchMethodException e) {
  • throw new InternalError(e.toString());
  • } catch (IllegalAccessException e) {
  • throw new InternalError(e.toString());
  • } catch (InstantiationException e) {
  • throw new InternalError(e.toString());
  • } catch (InvocationTargetException e) {
  • throw new InternalError(e.toString());
  • }
  • }
  • /**
  • * 判断某个类是否是代理类
  • *
  • * @param cl 测试的目标类
  • * @return
  • * @throws NullPointerException cl为null时
  • */
  • public static boolean isProxyClass(Class<?> cl) {
  • if (cl == null) {
  • throw new NullPointerException();
  • }
  • // 在proxyClasses中查找键
  • return proxyClasses.containsKey(cl);
  • }
  • /**
  • * 返回特定代理对象的调用处理器
  • *
  • * @param proxy 代理类的实例对象
  • * @return 该代理类的实例对象的调用处理器
  • * @throws IllegalArgumentException 当传入的proxy不是代理类对象时
  • */
  • public static InvocationHandler getInvocationHandler(Object proxy)
  • throws IllegalArgumentException {
  • // 首先判断传入的对象是否是代理类生成的对象
  • if (!isProxyClass(proxy.getClass())) {
  • throw new IllegalArgumentException("not a proxy instance");
  • }
  • // 直接返回h,即构造Proxy时传入的调用处理器
  • java.lang.reflect.Proxy p = (java.lang.reflect.Proxy) proxy;
  • return p.h;
  • }
  • // 本地方法,用于生成代理类
  • private static native Class defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
  • }

从上面源代码的分析可以得知,在使用动态代理获取代理对象时,有以下的几点需要注意的:

  1. 目标类实现的接口数量最多为65535个。
  2. 这些接口对指定的类加载器必须可见。
  3. 非公共接口必须位于同一包中。
  4. 在某些签名相同的接口中,如果任一方法的返回类型为基本类型或void,那么这些同名方法都必须具有相同的返回类型,否则其中一个方法必须拥有可以适用于所有其他同名方法的返回类型。
  5. 代理类扩展的方法必须是在目标类的接口中已经定义的方法,因为代理类继承自接口,目标类的接口中没有定义的方法在代理类中是没有的。

3.3. 生成的动态代理类详解

从上面的Proxy源代码中我们可以得知,关键代码byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);可以用于生成代理类的.class文件,只需要传入代理名称和接口集合即可,而这个代理名称是可以自定义的,接口集合也即是我们在使用Proxy.newProxyInstance()方法时传入的,因此我们可以直接使用这个方法生成代理类的.class文件进行分析,代码如下:

  • private static void getBugattiProxyClassFile() {
  • Bugatti bugatti = new Bugatti("Bugatti Chiron");
  • byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", bugatti.getClass().getInterfaces());
  • String path = "/Users/LennonChin/Desktop/BugattiProxyClass.class";
  • FileOutputStream fos = null;
  • try {
  • fos = new FileOutputStream(path);
  • fos.write(classFile);
  • fos.flush();
  • System.out.println("代理类class文件写入成功");
  • } catch (Exception e) {
  • System.out.println("写文件错误");
  • } finally {
  • if (fos != null) {
  • try {
  • fos.close();
  • } catch (IOException e) {
  • e.printStackTrace();
  • }
  • }
  • }
  • }

运行上述代码,可以得到一个BugattiProxyClass.class的文件,该文件反编译之后的内容如下:

注:下面的代码经过部分调整,调换了静态代码块和某些方法出现的位置。

  • //
  • // Source code recreated from a .class file by IntelliJ IDEA
  • // (powered by Fernflower decompiler)
  • //
  • import com.coderap.foundation.proxy.Car;
  • 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 Car {
  • private static Method m1;
  • private static Method m4;
  • private static Method m3;
  • private static Method m0;
  • private static Method m2;
  • static {
  • try {
  • m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
  • m4 = Class.forName("com.coderap.foundation.proxy.Car").getMethod("getInfo");
  • m3 = Class.forName("com.coderap.foundation.proxy.Car").getMethod("run");
  • m0 = Class.forName("java.lang.Object").getMethod("hashCode");
  • m2 = Class.forName("java.lang.Object").getMethod("toString");
  • } catch (NoSuchMethodException var2) {
  • throw new NoSuchMethodError(var2.getMessage());
  • } catch (ClassNotFoundException var3) {
  • throw new NoClassDefFoundError(var3.getMessage());
  • }
  • }
  • public $Proxy0(InvocationHandler var1) throws {
  • super(var1);
  • }
  • public final String getInfo() throws {
  • try {
  • return (String) super.h.invoke(this, m4, (Object[]) null);
  • } catch (RuntimeException | Error var2) {
  • throw var2;
  • } catch (Throwable var3) {
  • throw new UndeclaredThrowableException(var3);
  • }
  • }
  • 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 boolean equals(Object var1) throws {
  • try {
  • return (Boolean) super.h.invoke(this, m1, new Object[]{var1});
  • } catch (RuntimeException | Error var3) {
  • throw var3;
  • } catch (Throwable var4) {
  • throw new UndeclaredThrowableException(var4);
  • }
  • }
  • public final int hashCode() throws {
  • try {
  • return (Integer) super.h.invoke(this, m0, (Object[]) null);
  • } catch (RuntimeException | Error var2) {
  • throw var2;
  • } catch (Throwable var3) {
  • throw new UndeclaredThrowableException(var3);
  • }
  • }
  • public final String toString() throws {
  • try {
  • return (String) super.h.invoke(this, m2, (Object[]) null);
  • } catch (RuntimeException | Error var2) {
  • throw var2;
  • } catch (Throwable var3) {
  • throw new UndeclaredThrowableException(var3);
  • }
  • }
  • }

从反编译得到的代码可以对动态代理的底层实现一窥究竟。其中的静态代码块首先加载了java.lang.Objectcom.coderap.foundation.proxy.Car两个类获取相关的需要实现的方法,然后对这些方法都进行了不同程度的重写,在重写的方法中,使用super.hinvoke()方法对调用过程进行包装,由于$Proxy0类继承自Proxy类,因此super.h其实就是我们在创建代理对象的时候传入的InvocationHandler调用处理器(即前面Proxy类源代码分析的第49行)。