• Java设计模式-代理模式


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


    代理模式顾名思义,有个代理,那这个代理做什么工作呢?我们平时生活中,我们遇到过的代理就是各个品牌的代理,一个产品不能让广大的消费者去工厂去买吧,工厂生产出产品,各个地方有一个代理,代理这个工厂负责个每个商店推销产品。又比如一个歌手有一个助理,很多事情都是助理负责,助理接收到信息,看看是什么信息,自己可以干的就自己完成,就比如歌手的助理收到要去开演唱会的通知,助理不能唱歌啊,这个核心的事情还是得交给老板,助理的作用就是避免自己的老板受到打扰。所以代理是过滤掉一些事情,剩下的核心内容还是由真实的对象来负责。


    代理模式存在的意义就是,降低了真实对象和要做事情的耦合性,降低了他们之间的联系,代理类对请求进行过滤,预处理,将请求交给真实对象,或者委派给真实对象。好比消费者不会去工厂去买产品,一个歌手不会每天都会接收到很多没有用的信息。


    同样代理模式也符合开闭原则,对修改关闭,对扩展开放。在不改变真实类的情况下,对其类进行扩展。代理类对外提供一系列的接口,用户对其使用不会对原对象产生影响,对原对象有一个良好的封装性。


    代理模式分为静态代理和动态代理,还有一种是Cglib代理,这里就不介绍了。

    静态代理:

    抽象角色:是一个接口,定义了行为,没有具体实现

    package demo_staticagent;
    
    public interface AbstractSubject {
    
        public abstract void sing();
    }
    

    代理角色:其中有真实对象的引用,从而对真实对象进行操作,可以对真实对象的操作进行扩展,不会修改原对象。

    package demo_staticagent;
    
    public class ProxySubject implements AbstractSubject {
        
        private AbstractSubject abstractSubject;
    
        public ProxySubject(AbstractSubject abstractSubject) {
            this.abstractSubject = abstractSubject;
        }
    
    
        @Override
        public void sing() {
            abstractSubject.sing();
        }
    }
    

    真实对象:有对事件的真实操作,最终我们要引用的对象。
    package demo_staticagent;
    
    public class RealSubject implements AbstractSubject{
    
        @Override
        public void sing() {
            System.out.println("真唱");
        }
    }
    

    测试类:
    package demo_staticagent;
    
    public class Test {
    
        public static void main(String[] args) {
            RealSubject reaal = new RealSubject();
            ProxySubject proxy = new ProxySubject(reaal);
            proxy.sing();
    
        }
    }
    

    输出:
    真唱

    从输出结果可以看出,是真唱不是假唱。我们创建了真实对象和代理对象,用代理模式进行调用,通过代理最终我们核心的代码,执行的还是真实角色中的代码。知识对真实角色进行了封装。


    动态代理:

    动态代理相比于静态代理,动态代理的代理类是在动态生成的,也就是jvm通过反射获取代码生成代理类,所以用户并不能决定代理角色和真实角色之间的联系,而是由程序运行时候决定的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。


    动态代理实现的三步走:

    • 实现InvocationHandler接口,创建自己的调用处理器 。
    • 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类 。
    • 执行真实角色具体任务。

    要想创建处理器必须实现InvocationHandler接口,之后通过类装载器创建代理类。每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。


    Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    
    proxy:  指代我们所代理的那个真实对象
    method:  指代的是我们所要调用真实对象的某个方法的Method对象
    args:  指代的是调用真实对象某个方法时接受的参数


    我们依旧要创建抽象角色和真实角色类

    package demo_proxy;
    
    public interface AbstractSubject {
    
        public abstract void sing();
    }
    package demo_proxy;
    
    public class RealSubject implements AbstractSubject{
    
        @Override
        public void sing() {
            System.out.println("真唱");
        }
    }

    代理角色:和静态代理相比,静态代理运用对象调用完成代理,而动态代理通过实现InvocationHandler接口,创建了一个调用处理器,其实是通过Java的反射机制完成

    package demo_proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class ProxySubject implements InvocationHandler {
    
        public AbstractSubject abstractSubject;
    
        public ProxySubject(AbstractSubject abstractSubject) {
            this.abstractSubject = abstractSubject;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            method.invoke(abstractSubject, args);
            return null;
        }
    
    }
    
    当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。

    测试类:

    package demo_proxy;
    
    import java.lang.reflect.Proxy;
    
    public class Client {
        public static void main(String[] args) {
            /*代理的真实对象*/
            RealSubject real = new RealSubject();
            /*代理哪个对象就把真实对象传进去*/
            ProxySubject proxySubject = new ProxySubject(real);
             /*
             * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数
             * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象
             * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了
             * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上
             */
            AbstractSubject a = (AbstractSubject) Proxy.newProxyInstance(proxySubject.getClass().getClassLoader, 
                    real.getClass().getInterfaced(), proxySubject);
            a.sing();
        }
    }
    
    public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
    
    loader:  一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
    
    interfaces:  一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口
                  (多态),这样我就能调用这组接口中的方法了
    
    h:  一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上

    看上去实现很简单,我们只需要实现接口,调用其中的方法就可以实现动态代理。

    通过newProxyInstance方法获取代理类实例,而后我们便可以通过这个代理类实例调用代理类的方法,对代理类的方法的调用实际上都会调用中介类(调用处理器)的invoke方法。


    输出:

    真唱


    参考:

    http://www.cnblogs.com/xiaoluo501395377/p/3383130.html

    http://www.jb51.net/article/86531.htm


  • 相关阅读:
    JVM的即时编译器JIT之简单介绍
    JS脚本动态给标签控件添加事件
    getParameterMap的使用
    IOS开发中判断文件是否存在,不存在则拷贝
    javaweb中解决Cookie中文乱码问题
    网页中的上标和下标实现
    Java中枚举的使用
    ASP.NET 首页性能的4大做法
    httpHandlers和httpModules接口介绍 (5)
    css+div排版如何支持所有浏览器
  • 原文地址:https://www.cnblogs.com/duzhentong/p/8576527.html
Copyright © 2020-2023  润新知