美文网首页
设计模式 - 代理模式

设计模式 - 代理模式

作者: arrow_zh | 来源:发表于2019-08-14 17:47 被阅读0次

    1、代理模式定义

    给某一个对象提供一个代 理,并由代理对象控制对原对象的引用。代理模式的英 文叫做Proxy或Surrogate,它是一种对象结构型模式

    2、代理模式实现方式

    2.1、静态代理

    Person接口:

    public interface Person {
      /**
       * 租房
       */
      public void rentRoom();
    }
    

    张三实现Person接口:

    public class Zhangsan implements Person {
      @Override
      public void rentRoom() {
          System.out.println("张三租房需求:");
          System.out.println("套一,小区环境好");
          System.out.println("有空调,有洗衣机,有冰箱");
      }
    }
    

    代理类:

    public class RentProxy {
    
      private Person person;
    
      public RentProxy(Person person){
          this.person = person;
      }
    
      public void rentRoom(){
          System.out.println("租房中介开始给你找房子:");
          this.person.rentRoom();
          System.out.println("找房子结束, 开始入住");
      }
    }
    
    

    静态代理相对简单:只需代理类持有对象的引用即可,通过代理类调用被代理的方法。但是确定也比较明显:如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度

    2.1、动态代理(JDK方式)

    核心是通过Proxy的一个方法(newProxyInstance)来实现代理类,传递三个参数:
    第一个参数:ClassLoader即类加载器;
    第二个参数为 Class<?>[] interfaces即需要被代理对象实现的接口
    第三个参数为:InvocationHandler h即实现InvocationHandler 接口的类,通过InvocationHandler 的invoke方法回调
    JdkProxy类实现:

    public class JdkProxy {
    
        private Object target;
    
        public JdkProxy(Object target){
            this.target = target;
        }
    
        /**
         * 获取代理对象类
         * @return
         */
        public Object getProxyInstance(){
            return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("JDK动态代理租房中介开始给你找房子:");
                    Object obj = method.invoke(target, args);
                    System.out.println(proxy.getClass());
                    System.out.println("找房子结束, 开始入住");
                    return obj;
                }
            });
        }
    }
    

    Main方法测试

        public static void main(String[] args) {
            //设置输入生成的代理类$Proxy0
            System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    
            JdkProxy jdkProxy = new JdkProxy( new Zhangsan());
            Person person = (Person) jdkProxy.getProxyInstance();
            person.rentRoom();
        }
    
    分析执行逻辑(基于JDK1.8)

    a、查看Proxy.newProxyInstance的方法

    //只看核心代码 通过getProxyClass0生成Class对象
    Class<?> cl = getProxyClass0(loader, intfs);
    

    b、查看getProxyClass0方法生成

        private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
            //如果在proxyClassCache中有则直接取出,如果不存在则通过ProxyClassFactory生成
            return proxyClassCache.get(loader, interfaces);
        }
    

    c、查看ProxyClassFactory类,ProxyClassFactory类是Proxy的内部类,其中核心方法为apply前面大部分都是校验,判断我看查看核心代码

    //通过ProxyGenerator生成$Proxy0的二进制数组
     byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
    
    //加载二进制数据到内存中,
     return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
    
    总结:

    JDK自带实现代理是通过Proxy类来创建一个$Proxy0(备注:0是一个自增数字 根据生成的代理类数量),
    最后放出生成的$Proxy0.class的反编译文件,从编译的文件可以看出代理类基础了Proxy类所以被代理对象必须实现接口

    //
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    //
    
    package com.sun.proxy;
    
    import io.arrow.proxy.pattern.staticed.Person;
    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 Person {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
    
        public $Proxy0(InvocationHandler var1) throws  {
            super(var1);
        }
    
        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 void rentRoom() 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 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);
            }
        }
    
        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);
            }
        }
    
        static {
            try {
                m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                m3 = Class.forName("io.arrow.proxy.pattern.staticed.Person").getMethod("rentRoom");
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
    2.1、动态代理(CGLIB方式)

    首先需要在工程中引入cglib.jar 和 asm.jar,我在这里导入的是cglib-nodep.jar文件,因为cglib-nodep.jar包含了cglib相关的包。
    cglib可以代理接口或者普通类,直接上代码:

        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(SampleClass.class);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    System.out.println("before");
                    Object res = methodProxy.invokeSuper(o, objects);
                    System.out.println(res);
                    System.out.println("after");
                    return res;
                }
            });
            SampleClass sampleClass = (SampleClass) enhancer.create();
            sampleClass.sayHello("Zhang San");
        }
    

    其中我看可以看到主要的类Enhancer 类似于JDK代理中的Proxy类,
    第一步:Enhancer 通过setSuperclass或者setInterfaces设置需要代理的接口或者方法。
    第二步:设置Callback我看可以通过源代码查看Callback接口是一个空的接口没有定义任何方法只是一个标记接口包含的实现有

    类图
    第二步:调用create方法生成代理类。当然代理类是基础父类或者是实现传入的接口。
    总结:
    CGLIG动态代理方式其实也是利用底层动态生成class文件然后加载到内存中,原理与JDK动态代理类似

    相关文章

      网友评论

          本文标题:设计模式 - 代理模式

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