0%

深入浅出动态代理

前言

面试中经常被问到动态代理相关内容,每次都答得不够完美,又因近期在尝试手写RPC框架,了解到不少的动态代理相关的内容,顺道总结一下,于是就有了这篇文章

静态代理? 动态代理!

区别

静态代理是在编译期间就生成了实际的字节码和对应的class文件,而动态代理可以在运行中动态生成代理类。

  • 静态代理示例
    public interface Service {
        void perform();
    }
    // 实际类
    public class RealService implements Service {
        @Override
        public void perform() {
            System.out.println("Performing service...");
        }
    }
    // 代理类也要实现和被代理的类实现的同一个接口才能进行代理
    public class StaticProxy implements Service {
    	// 将需要代理的类封装到自己的内部
        private final Service realService;
    	
        public StaticProxy(Service realService) {
            this.realService = realService;
        }
    	// 实际就是在实现的方法中进行代理操作,然后执行被代理的类的方法
        @Override
        public void perform() {
            System.out.println("Static Proxy: Before performing service");
            realService.perform();
            System.out.println("Static Proxy: After performing service");
        }
    }
    实际上就是通过将被代理类封装到内部,然后调用。缺点很明显:没新增一个代理都需要重新新建一个类然后重新写一个包装。
  • 动态代理实例(JDK代理)
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public interface Service {
        void perform();
    }
    
    public class RealService implements Service {
        @Override
        public void perform() {
            System.out.println("Performing service...");
        }
    }
    
    public class DynamicProxyHandler implements InvocationHandler {
        private final Object target;
    
        public DynamicProxyHandler(Object target) {
            this.target = target;
        }
    	// 
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Dynamic Proxy: Before performing service");
            Object result = method.invoke(target, args);
            System.out.println("Dynamic Proxy: After performing service");
            return result;
        }
    }
    
    public class DynamicProxyDemo {
        public static void main(String[] args) {
            Service realService = new RealService();
            Service proxyInstance = (Service) Proxy.newProxyInstance(
                    realService.getClass().getClassLoader(),
                    realService.getClass().getInterfaces(),
                    new DynamicProxyHandler(realService)
            );
    
            proxyInstance.perform();
        }
    }

源码解读

参考

解密Proxy

(以下是基于Java8的源码)
我们在使用JDK动态代理的时候使用的最多的就是Proxy.newProxyInstance()方法,接下来我们就来解密这个方法是如何实现我们的动态代理。

@CallerSensitive
   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);

       /*
        * Invoke its constructor with the designated invocation handler.
        */
       try {
           if (sm != null) {
               checkNewProxyPermission(Reflection.getCallerClass(), cl);
           }

           final Constructor<?> cons = cl.getConstructor(constructorParams);
           final InvocationHandler ih = h;
           if (!Modifier.isPublic(cl.getModifiers())) {
               AccessController.doPrivileged(new PrivilegedAction<Void>() {
                   public Void run() {
                       cons.setAccessible(true);
                       return null;
                   }
               });
           }
           return cons.newInstance(new Object[]{h});
       } catch (IllegalAccessException|InstantiationException e) {
           throw new InternalError(e.toString(), e);
       } catch (InvocationTargetException e) {
           Throwable t = e.getCause();
           if (t instanceof RuntimeException) {
               throw (RuntimeException) t;
           } else {
               throw new InternalError(t.toString(), t);
           }
       } catch (NoSuchMethodException e) {
           throw new InternalError(e.toString(), e);
       }
   }

参数解析:

  • loader:类加载器,用于自定义加载类
  • interface 接口数组,代理类将实现这些接口
  • h 调用处理器,处理代理实例上的方法调用
    这段代码的主要意思是获取调用者类和代理类构造函数。
    接着关注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);
       }
    接着看proxyClassCache
    // 使用WeakCache降低出现内存泄漏的概率
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>  
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
    核心是ProxyClassFactroy,让我们关注它
    // 核心是实现BiFunction
    /**
     * ProxyClassFactory 是一个工厂类,用于生成和定义代理类。
     * 它实现了 BiFunction 接口,接受 ClassLoader 和接口数组作为输入,
     * 返回生成的代理类。
     */
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
    
        // 所有代理类名称的前缀
        private static final String proxyClassNamePrefix = "$Proxy";
    
        // 用于生成唯一代理类名称的下一个数字
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
        /**
         * 生成并定义代理类。
         *
         * @param loader 用于定义代理类的��加载器
         * @param interfaces 代理类要实现的接口数组
         * @return 生成的代理类
         * @throws IllegalArgumentException 如果接口数组中的任何接口不可见或不是接口,或接口重复
         */
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
    
            // 使用 IdentityHashMap 检查接口是否重复
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * 验证类加载器是否将此接口的名称解析为相同的 Class 对象。
                 */
                Class<?> interfaceClass = null;
                try {
    	            // 对于每一个使用指定的加载器去加载他的对象
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader: " + loader);
                }
                /*
                 * 验证 Class 对象是否实际代表一个接口。
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * 验证此接口是否重复。
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
    
            String proxyPkg = null;     // 定义代理类的包
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
            /*
             * 记录非公共代理接口的包,以便代理类将在同一包中定义。
             * 验证所有非公共代理接口是否在同一包中。
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.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) {
                // 如果没有非公共代理接口,使用 com.sun.proxy 包
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
    
            /*
             * 选择要生成的代理类的名称。
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
            /*
             * 生成指定的代理类。
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * 这里的 ClassFormatError 意味着(除非代理类生成代码中有错误),
                 * 否则是代理类创建时提供的参数存在其他无效方面(例如虚拟机限制超出)。
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }
    BiFunction 是 Java 8 引入的一个函数式接口,位于 java.util.function 包中。它代表一个接受两个输入参数并返回一个结果的函数。BiFunction 接口定义如下:
    @FunctionalInterface
    public interface BiFunction<T, U, R> {
        R apply(T t, U u);
    
        // 其他默认方法
    }
    然后我们顺着往下找,通过generateProxyClass ->generateClassFile ->generateMethod
    代码过长这里只给出比较重要的部分
    {
    	// 获取InvocationHandler字段
    	 out.writeByte(opc_getfield);
                out.writeShort(cp.getFieldRef(
                    superclassName,
                    handlerFieldName, "Ljava/lang/reflect/InvocationHandler;"));
    	// 加载Method对象
                code_aload(0, out);
    
                out.writeByte(opc_getstatic);
                out.writeShort(cp.getFieldRef(
                    dotToSlash(className),
                    methodFieldName, "Ljava/lang/reflect/Method;"));
    	// 后面紧跟处理方法参数的部分,这部分省略
    
    	// 重点!
    	// 调用InvocationHandler.invoke方法
    	 out.writeByte(opc_invokeinterface);
                out.writeShort(cp.getInterfaceMethodRef(
                    "java/lang/reflect/InvocationHandler",
                    "invoke",
                    "(Ljava/lang/Object;Ljava/lang/reflect/Method;" +
                        "[Ljava/lang/Object;)Ljava/lang/Object;"));
                out.writeByte(4);
                out.writeByte(0);
        // 后面设置返回值和异常处理等信息,最后返回方法的信息
    	return minfo;
    }
    到这里我们才终于发现JDK动态代理的原理:
    原来就是在代理方法中通过Proxy引用了自定义的InvocationHandler!!
    通过Proxy.newProxyInstance()方法将invocationHandler传入,然后生成代理类来继承Proxy类,从而拿到InvocationHandler,最后在代理类中调用invoke()方法