zoukankan      html  css  js  c++  java
  • 代理模式

    代理模式

    代理模式介绍

    为什么要用代理模式?

    代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问
    在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介作用

    代理模式种类

    如果按照代理创建的时期来进行分类的话,可以分为两种:静态代理、动态代理。
    静态代理是由程序员创建或特定工具自动生成源代码,在对其编译。在程序员运行之前,代理类.class文件就已经被创建了。
    动态代理是在程序运行时通过反射机制动态创建的。

    代理模式的角色

    抽象角色:声明真实对象和代理对象的共同接口:(租房子)
    代理角色:代理对象内部含有真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便 在任何时刻都能代理真实对象。同时,代理对象可以在执行真实对象操作时,附加其他操作,相当于对真实对象进行封装:中介
    真实角色:实现抽象角色,也就是定义真实角色所需要实现的业务逻辑(房主)

    静态代理

    抽象角色

    //也就是需要为这个对象的进行代理,因为接口中有方法,接口又是由其他对象实现其中的方法,那么我们可以为这个方法增强一些功能
    public interface Subject {
        void request();
    }
    

    具体角色:实现抽象角色接口(或抽象类),也就是真正的实现逻辑,我们代理就是在这个实现逻辑的基础上增强一些功能

    public class RealSubject implements Subject {
    	//在这里实现真正的逻辑
        @Override
        public void request() {
            System.out.println("一个真实的角色");
        }
    }
    

    代理对象:代理对象中肯定是需要具体角色的一个引用的,要不然没法操纵具体角色的

    public class DynamicSubject implements Subject {
        private RealSubject realSubject;
        public DynamicSubject(RealSubject realSubject) {
            this.realSubject = realSubject;
        }
    
        @Override
        public void request() {
            System.out.println("代理前。。。。在这里可以干一些事情");
            //通过具体角色的引用来操作具体角色
            realSubject.request();
            System.out.println("代理后。。。在这里也可以干一些事情");
        }
    }
    

    存在的问题:
    静态代理:真实角色必须实现已经存在的,并将其作为代理对象的内部属性。但是实际使用时,一个真实角色必须对应一个代理角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决

    动态代理(JDK代理)

    InvocationHandler

    /**
     * {@code InvocationHandler} is the interface implemented by
     * the <i>invocation handler</i> of a proxy instance.
     * <p>Each proxy instance has an associated invocation handler.
     * When a method is invoked on a proxy instance, the method
     * invocation is encoded and dispatched to the {@code invoke}
     * method of its invocation handler.
     */
    //每一个动态代理类的调用处理程序都必须实现InvocationHandler接口,并且每个代理类的实例都关联到了
    //实现该接口的动态代理类调用处理程序中,当我们通过动态代理对象调用一个方法时候,这个方法的调用就会
    //被转发到实现InvocationHandler接口类的invoke方法来调用
    public interface InvocationHandler {
    	public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
    }
    /**
    	关于Method中invoke方法
    	public Object invoke(Object obj, Object... args)
    	它在这里有两个版本的实现
    	1.最终调用的一个native方法invoke0,它在HotSpot JVM里调用JVM_InvokeMethod函数
    	2.Java版实现:这里运行了asm动态生成字节码技术
    **/
    

    Proxy

    public class Proxy implements java.io.Serializable {
    	 //获得一个代理类,其中loader时类加载,interfaces是真实类所拥有的全部接口的数组
    	  public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)
    	 //返回代理类的一个实例,返回后的代理类可以当作被代理类使用
    	  public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
    }
    

    抽象角色

    public interface Subject {
        void request();
    }
    
    public class RealSubject implements Subject {
        @Override
        public void request() {
            System.out.println("一个真实的角色");
        }
    }
    

    代理角色

    /**
     * 代理角色:肯定需要一个真实对象的引用,要不然怎么操作真实角色呢?
     */
    public class DynamicSubject implements InvocationHandler {
        //代理角色中含有真实角色
        //这里为什么使用Object类型呢?为了避免为每一个真实对象创建一个代理对象,
        // 这样的话我们可以传递任意的真实对象
        private Object sub;
    
        public DynamicSubject(Object sub) {
            this.sub = sub;
        }
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("before calling: " + method);
            //通过反射技术,调用真实对象的方法
            method.invoke(this.sub, args);
            System.out.println("after calling: " + method);
            return null;
        }
    }
    

    Client

    public class Client {
        public static void main(String[] args) {
    //  System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
            RealSubject rs = new RealSubject();
            InvocationHandler ds = new DynamicSubject(rs);
            Class<? extends RealSubject> cls = rs.getClass();
            /**
             //Generate the specified proxy class.
             byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
             proxyName, interfaces, accessFlags);
             */
            Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(),
                    cls.getInterfaces(),ds);
            //在这里会将其上交给InvocationHandler处理     
            subject.request();
            System.out.println(subject.getClass()); //class com.sun.proxy.$Proxy0
        }
    }
    

    从字节码分析JDK动态代理

    在JDK1.8中对于基于JDK的动态代理代码有所修改,调用了函数式编程

    //从Proxy的newProxyInstance开始
     public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h){
          //。。。。
         Class<?> cl = getProxyClass0(loader, intfs);
         //。。。。
    }
    private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
            return proxyClassCache.get(loader, interfaces);
    }
    //最终DEBUG我们可以得到
    public Class<?> apply(ClassLoader loader, Class<?>[] interfaces){
    	/*
      	 * Generate the specified proxy class.
        */
        //也就是这里会生成字节码文件
       byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
           proxyName, interfaces, accessFlags);
    }
    public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
            ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
            final byte[] var4 = var3.generateClassFile();
            //private static final boolean saveGeneratedFiles = 
            //(Boolean)AccessController.doPrivileged(new 
            //GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
            //通过这里我们可以分析出,如果我们将系统参数
            //sun.misc.ProxyGenerator.saveGeneratedFiles设置为true的话,我们就可以将字节码文件写入磁盘中,从而得到字节码文件
            if (saveGeneratedFiles) {
            	//这里会产生文件
            }
        }
    

    分析com.sun.proxy.$Proxy0字节码文件

    //通过设置该系统属性,得到字节码文件
    //System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
    

    经过IDEA反编译

    /*
    	通过分析这个字节码文件我们可以得出
    	1.对于Object中的方法,我们只对equals,toString,hashCode三个方法进行了代理,对于其他方法是没有进行代理的
    	2.public final class $Proxy0 extends Proxy implements Subject从这里可以看出我们是实现了抽象接口的角色。这也就是为什么Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(),
                    cls.getInterfaces(),ds);可以进行强制转换了
    */
    public final class $Proxy0 extends Proxy implements Subject {
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    	//return cons.newInstance(new Object[]{h});这里就是一个参数的构造方法,在构造的时候将实现了InvocationHandler的具体实现类传递进去
        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 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 void request() throws  {
            try {
            	//super.h就是Proxy中的protected InvocationHandler h;
            	//而这个h就是我们在newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)我们自己实现了InvocationHandler的具体实现
                super.h.invoke(this, m3, (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"));
                m2 = Class.forName("java.lang.Object").getMethod("toString");
                m3 = Class.forName("com.chen.jvm.bytecode.Subject").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());
            }
        }
    }
    
  • 相关阅读:
    数据库_初学
    数据库—修改表的列
    做一个导航栏(bootstrap)
    几个比较常用的特效
    当、你想给一个目标挂上一个事件时
    图片轮播的几个小程序
    JS练习题 ( 下拉菜单;好友选中输入)
    Bootstrap 按钮和折叠插件
    Bootstrap 轮播插件
    Bootstrap 弹出框和警告框插件
  • 原文地址:https://www.cnblogs.com/liuligang/p/10516824.html
Copyright © 2011-2022 走看看