zoukankan      html  css  js  c++  java
  • 阴阳大论之代理模式

    阴阳大论之代理模式

    目录

    代理模式


    定义

    在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

    优缺点

    • 1、职责清晰。
    • 2、高扩展性。
    • 3、智能化。
    • 1、由于在客户端和真实主题之间增加了代理对象,因此有些类型的代理模式可能会造成请求的处理速度变慢。
    • 2、实现代理模式需要额外的工作,有些代理模式的实现非常复杂。

    使用场景

    • 远程代理

    为一个位于不同的地址空间的对象提供一个局域代表对象。这个不同的地址空间可以是本电脑中,也可以在另一台电脑中。最典型的例子就是——客户端调用Web服务或WCF服务。

    • 虚拟代理(Virtual PRoxy)会

    推迟真正所需对象实例化时间. 在需要真正的对象工作之前, 如果代理对象能够处理, 那么暂时不需要真正对象来出手.
    优点: 在应用程序启动时,由于不需要创建和装载所有的对象,因此加速了应用程序的启动。

    • Copy-on-Write 代理

    虚拟代理的一种,把复制(或者叫克隆)拖延到只有在客户端需要时,才真正采取行动。

    • 保护(Protect or Access)代理

    控制一个对象的访问,可以给不同的用户提供不同级别的使用权限。

    • Cache代理

    为某一个目标操作的结果提供临时的存储空间,以便多个客户端可以这些结果。

    • 防火墙(Firewall)代理

    保护目标不让恶意用户接近

    • 同步化(Synchronization)代理

    使几个用户能够同时使用一个对象而没有冲突。

    • 智能引用(Smart Reference)代理

    当一个对象被引用时,提供一些额外的操作,比如将对此对象调用的次数记录下来等

    与其它模式的区别

    • 和适配器模式的区别

    适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。

    • 和装饰器模式的区别

    装饰器模式为了增强功能,而代理模式是为了加以控制。

    静态代理

    JDK实现动态代理

    实现流程

    • 实现InvocationHandler接口,创建自己的调用处理器
    • 调用Proxy的静态方法,创建代理类并生成相应的代理对象
    • 源码demo
    /**
     * 经纪人,动态代理
     *
     * @author 石玉森
     * @create 2018-10-19 11:51
     **/
    
    public class JDKDynamicProxyFactory<T> implements InvocationHandler {
    
        /**
         * 委托类实例:被代理的真实对象实例
         */
        private T target;
    
        public JDKDynamicProxyFactory(T target) {
            this.target = target;
        }
    
        /**
         *
         * @param proxy    生成的代理类实例
         * @param method
         * @param args
         * @return
         * @throws Throwable
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("调用被被代理的方法 before");
            Object result = method.invoke(target,args);
            System.out.println("调用被被代理的方法 after");
            return result;
        }
    
        /**
         * 创建代理类实例
         * @return
         */
        public T getProxyInstance(){
            ClassLoader classLoader = target.getClass().getClassLoader();
            Class[] interfaces = target.getClass().getInterfaces();
            Object proxy = Proxy.newProxyInstance(classLoader,interfaces,this);
            return (T) proxy;
        }
    }
    
    

    实现原理

    JDK动态代理是基于java反射来实现。

    1. 创建代理类的源码;

    拿到被代理类实现的接口类对象,遍历里面的方法,以字符串的形式拼凑出代理类源码(动态代理类与被代理类实现同一接口在此体现),将代理类的源码写到本地java文件

    1. 将源码进行编译成字节码;

    读取源码,编译java文件,得到.class字节码文件(的路径)

    1. 将字节码加载到内存;
    2. 实例化代理类对象并返回给调用者。

    CGLIB实现动态代理

    定义

    • CGLIB是一个高性能的代码生成类库,被Spring广泛应用。其底层是通过ASM字节码框架生成类的字节码,达到动态创建类的目的。

    实现流程

    • 实现MethodInterceptor接口,创建自己的调用处理器
    • 通过Enhancer类增强工具, 创建代理类并生成相应的代理对象
    • 源码demo
    **
     * cglib动态代理实现
     *
     * @author 石玉森
     * @create 2018-10-19 17:01
     **/
    
    public class CglibDynamicProxyFactory<T> implements MethodInterceptor {
        /**
         * 被代理类:委托类
         */
        private T target;
        /**
         * @param o           代理的对象(生成的被代理类的子类实例)
         * @param method      被代理的方法
         * @param objects     被代理的方法的参数
         * @param methodProxy 代理的对的方法(生成的被代理类的子类实例)
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            Object result = null;
            System.out.println("调用被被代理的方法 before");
    //        result = method.invoke(o,objects);//不对
            result = method.invoke(this.target, objects);//可以
    //        result = methodProxy.invoke(this.target, objects);//可以
    //        result = methodProxy.invokeSuper(o, objects);//可以
            System.out.println("调用被被代理的方法 after");
            return result;
        }
        public T getProxyInstance(T target) {
            this.target=target;
            Enhancer enhancer = new Enhancer();
            //设置创建子类的类,即指定为哪个类产生代理类
            enhancer.setSuperclass(target.getClass());
            /*设置回调函数 setCallback设置被代理类的public非final方法被调用时的处理类
             * */
            enhancer.setCallback(this);
            //通过字节码技术动态创建子类实例
            return (T) enhancer.create();
        }
    }
    
    

    实现原理

    • 生成代理类的二进制字节码文件;
    • 加载二进制字节码,生成Class对象( 例如使用Class.forName()方法 );
    • 通过反射机制获得实例构造,并创建代理类对象

    静态代理、CGLIB与JDK实现动态代理的区别

    代理模式 优点 缺点
    静态代理 简单 代码不能复用
    JDK动态代理 动态代码生成快 执行慢,强制实现接口
    CGLIB动态代理 生成代码慢 执行快,不需实现接口

    代理模式在spring中的应用

    spring AOP默认使用JDK动态代理实现。可以通过配置强制使用cglib代理。

    CopyOnWriteArrayList类使用代理模式实现

    /***
    * 静态代理:实现List
    * 随机存取:实现RandomAccess
    * 克隆:实现Cloneable
    */
    public class CopyOnWriteArrayList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
        final transient ReentrantLock lock = new ReentrantLock();
        //委托类。初始化时即实例化,不像虚拟代理懒加载
        private transient volatile Object[] array;
    
        public boolean add(E e) {
            final ReentrantLock lock = this.lock;
            lock.lock();
            try {
                Object[] elements = getArray();
                int len = elements.length;
                Object[] newElements = Arrays.copyOf(elements, len + 1);
                newElements[len] = e;
                setArray(newElements);
                return true;
            } finally {
                lock.unlock();
            }
        }
    }
    

    代理模式在Struts2中的应用

    struts2中的拦截器实现

    代理模式在RPC中的应用

    /**
    * 服务生产者
    * 服务生产者接受到消费方的调用,通过反射调起实际接口
    */
    ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
    try {
        String interfaceName = input.readUTF();
        String methodName = input.readUTF();
        Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
        Object[] arguments = (Object[]) input.readObject();
        ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
    
        try {
            if (!interfaceName.equals(interfaceClazz.getName())) {
                throw new IllegalAccessException("Interface wrong, export:" + interfaceClazz  + " refer:" + interfaceName);
            }
            Method method = service.getClass().getMethod(methodName, parameterTypes);
            Object result = method.invoke(service, arguments);
            output.writeObject(result);
        } catch (Throwable t) {
            output.writeObject(t);
        } finally {
            output.close();
        }
    } finally {
        input.close();
    }
    
    /**
    * 服务消费方
    * 服务消费方在本地通过代理的模式实例化代理类,代理类内部通过socket调用远程生产者
    */
    return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[]{interfaceClass},
        new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket =null;
                try {
                    socket = new Socket(host, port);
                    ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                    try {
                        output.writeUTF(method.getName());
                        output.writeObject(method.getParameterTypes());
                        output.writeObject(args);
                        ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                        try {
                            Object result = input.readObject();
                            return result;
                        } finally {
                            input.close();
                        }
                    } finally {
                        output.close();
                    }
                } finally {
                    socket.close();
                }
            }
        });
    
  • 相关阅读:
    iframe,modaldialog父子窗口相互通信的问题
    bootstrap基础知识
    基于原生js的图片延迟加载
    通过原生js的ajax或jquery的ajax获取服务器的时间
    转 fiddler教程
    正则表达式
    es6 对象
    ES6
    axios
    javascript闭包和闭包的几种写法和用法
  • 原文地址:https://www.cnblogs.com/shiyusen/p/10515466.html
Copyright © 2011-2022 走看看