美文网首页
Java 代理使用与原理

Java 代理使用与原理

作者: yangjingqzp | 来源:发表于2021-04-05 11:32 被阅读0次

    代理 指的是代表授权方执行处理事务。在编程中,通常是通过一个代理对象代表目标对象去执行方法,是对调用目标的一个包装。这样来保证目标对象方法的安全性、或者增强目标对象的方法功能。

    Java 有 3 种代理方式:

    静态代理

    通过手动创建代理对象,来实现对目标对象的代理。在这过程中,一般存在 3 种对象:客户端、目标对象、代理对象。
    客户端通过代理对象去调用目标对象的真实方法。
    对象的接口

    public interface IService {
        void request();
    }
    

    真正的目标对象

    public class RealService implements IService {
        @Override
        public void request() {
            System.out.println("真正处理请求内容");
        }
    }
    

    代理类

    public class ProxyService implements IService {
        private IService realService;
    
        public ProxyService(IService service) {
            this.realService = service;
        }
    
        @Override
        public void request() {
            // ...执行真实 request() 前其他逻辑
            System.out.println("request() 方法前逻辑");
            this.realService.request();
            System.out.println("request() 方法后逻辑");
            // ...执行真实 request() 后其他逻辑
        }
    }
    

    客户端实现

    public class StaticProxyClient {
        public void callRequest() {
            // 目标对象
            IService realService = new RealService();
            // 代理对象
            IService proxyService = new ProxyService(realService);
            // 通过代理对象执行真正的逻辑,并且在代理对象中对请求进行增强
            proxyService.request();
        }
    }
    

    静态代理需要手动地为每个目标类创建代理类,而代理类中的逻辑都是类似的简单的,为了更简单地使用代理出现了动态代理,通过在运行期动态创建接口对象的代理,避免了手动去创建静态代理类。
    动态代理是在运行时动态生成的,即编译完成后没有实际的 class 文件,而是在运行时动态生成类字节码,并加载到 JVM 中。

    JDK 原生动态代理

    JDK 提供了 Proxy.newProxyInstance() 来创建动态代理。实现步骤如下:

    • 创建被代理的目标类及它的接口
    • 创建接口 InvocationHandler 的实现类,它必须实现 invoke 方法
    • 通过 Proxy 的静态方法 newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理对象
    • 通过代理对象调用方法,代理的方法会被 InvocationHandler 的实现类接管
    // 实现 InvocationHandler 类,它的 invoke() 方法会接管代理调用的方法
    public class MyInvocationHandler implements InvocationHandler {
        private Object target;
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("代理方法前逻辑");
            Object result = method.invoke(this.target, args);
            System.out.println("代理方法后逻辑");
            return result;
        }
    }
    
    // JDK 动态代理测试
    public class JDKDynamicProxyClient {
        public static void main(String[] args) {
            RealService realService = new RealService();
            InvocationHandler handler = new MyInvocationHandler(realService);
    
            IService service = (IService) Proxy.newProxyInstance(
                // 通常是接口类的 ClassLoader
                IService.class.getClassLoader(),
                // 要实现的接口数组
                new Class[]{IService.class},
                // 代理用来处理调用方法的 InvocationHandler
                handler
            );
    
            // 通过代理类调用方法
            service.request();
        }
    }
    
    

    原理分析

    主要是看 Proxy.newProxyInstance() 方法。

    • Proxy 类的静态变量加载时初始化 proxyClassCache,代理类缓存中初始化代理类的创建工厂为 ProxyClassFactory()
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
            proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
    
    • Proxy.newProxyInstance() 静态方法返回代理类对象,它的调用过程如下:
    Proxy.newProxyInstance()  
          Proxy.getProxyClass0()
            ProxyClassFactory.get() #先从代理类缓存中查询,没有时从代理类工厂中创建
                ProxyClassFactory.apply()   #代理类工厂创建
                    ProxyGenerator.generateProxyClass()  #代理类生成器,生成代理类代码
                ProxyClassFactory.defineClass0()    #加载生成的 class 文件到 jvm,java 接着就能调用了
    

    最终生成的代理类关键代码如下:

    // 代理类继承 Proxy 类,并实现 IService 接口
    // 代理类名称固定位 "com.sun.proxy.$Proxy"+自增数字
    public final class com.sun.proxy.$Proxy0 extends Proxy implements IService {
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    
        // 构造函数中设置代理类的 InvocationHandler
        public com.sun.proxy.$Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        // ...
    
        public final void request() throws  {
            try {
                // 实际是 handler 中调用目标类的方法
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                // 代理的方法
                m3 = Class.forName("proxy.IService").getMethod("request");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    

    当代理执行到 request() 方法时,实际执行的是 InvocationHandler 的 invoke() 方法,并在方法中通过反射调用目标类的真实方法。

    JDK 动态代理要求被代理的对象必须有对应的一个或多个接口,不能给原类生成动态代理类。

    CGLIB 动态代理

    CGLIB 是一个强大的高性能代码生成包,可以在运行期转换字节码并生成目标类的子类。
    CGLIB 创建代理类对象是通过 Enhancer 类实现的,创建代理的主要步骤:

    • 创建被代理的目标类
    • 创建 Enchaner 类对象,设置对象的父类(为被代理的目标类)及 Callback 对象,通过 Enchaner 类的 create() 方法创建代理类对象
    • 通过代理对象调用方法
    // 被代理的类不需要实现接口,但是被代理的方法和类不能是 final
    public class RequestService {
        public void request() {
            System.out.println("真正处理请求内容");
        }
    }
    
    // CGLIB 代理测试
    public class CGLIBProxyClient {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(RequestService.class);
            // 这里是一个匿名类,被代理方法的处理类,需要实现 MethodInterceptor 接口的 intercept 方法
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects,
                    MethodProxy methodProxy) throws Throwable {
                    System.out.println("真实方法前逻辑...");
                    methodProxy.invokeSuper(o, objects);
                    System.out.println("真实方法后逻辑...");
                    return null;
                }
            });
            RequestService requestService = (RequestService) enhancer.create();
            requestService.request();
        }
    }
    

    在项目中通过增加下面设置,可以将生成的动态类保存到代码的根目录

    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./");
    

    运行创建代理后,就可以在项目根目录中看到 enhancer.create 创建的动态类,它的关键代码如下:

    // 继承目标类 RequestService
    public class RequestService$$EnhancerByCGLIB$$6a2da525 extends RequestService implements Factory {
        // 重写了 request() 方法
        public final void request() {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
    
            // enhancer.setCallback() 已经设置 callback,所以这里不为空
            if (var10000 != null) {
                // 调用 callback 中实现的 intercept() 方法
                var10000.intercept(this, CGLIB$request$0$Method, CGLIB$emptyArgs, CGLIB$request$0$Proxy);
            } else {
                super.request();
            }
        }
    }
    
    

    在 Callback 中通过 methodProxy.invokeSuper(o, objects); 来调用目标类的方法

    public class MethodProxy {
        public Object invokeSuper(Object obj, Object[] args) throws Throwable {
            try {
                // 初始化代理类、目标类信息
                // fci.f2 为代理类; fci.i2 为代理类中的代理方法的方法下标
                init();
                FastClassInfo fci = fastClassInfo;
                // 代理类通过下标调用代理方法
                return fci.f2.invoke(fci.i2, obj, args);
            }
            catch (InvocationTargetException e) {
                throw e.getTargetException();
            }
        }
    }
    

    自动生成的 FastClass 类主要代码如下:

    // 继承 FastClass 类
    public class RequestService$$FastClassByCGLIB$$86d56cc6 extends FastClass {
    
        // 根据方法签名的 hash 值映射方法的下标
        public int getIndex(String var1, Class[] var2) {
            switch(var1.hashCode()) {
            // ...
            case 1095692943:
                if (var1.equals("request")) {
                    switch(var2.length) {
                    case 0:
                        return 0;
                    }
                }
            }
    
            return -1;
        }
    
        // 代理类实际调用的地方,通过代理方法的下标调用目标类方法(不是反射方式)
        public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
            RequestService var10000 = (RequestService)var2;
            int var10001 = var1;
    
            try {
                switch(var10001) {
                case 0:
                    var10000.request();
                    return null;
                case 1:
                    return new Boolean(var10000.equals(var3[0]));
                case 2:
                    return var10000.toString();
                case 3:
                    return new Integer(var10000.hashCode());
                }
            } catch (Throwable var4) {
                throw new InvocationTargetException(var4);
            }
    
            throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
    }
    

    最后

    有问题,欢迎留言交流

    参考内容

    https://time.geekbang.org/column/article/10076
    https://zhuanlan.zhihu.com/p/35144462

    相关文章

      网友评论

          本文标题:Java 代理使用与原理

          本文链接:https://www.haomeiwen.com/subject/zfcdkltx.html