• 设计模式——代理模式


    一、代理模式简介

      在某些情况下,我们不希望或是不能直接访问对象 A,而是通过访问一个中介对象 B,由 B 去访问 A 达成目的,这种方式我们就称为代理。这里对象 A 所属类我们称为委托类,也称为被代理类,对象 B 所属类称为代理类。

      代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。

      在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

      根据程序运行前代理类是否已经存在,可以将代理分为静态代理和动态代理。

      代理优点有:

    • 隐藏委托类的实现
    • 解耦,不改变委托类代码情况下做一些额外处理,比如添加初始判断及其他公共操作

    二、静态代理

      代理类在程序运行前已经存在的代理方式称为静态代理。

    1.代理模式一般涉及到的角色

    抽象角色:声明真实对象和代理对象的共同接口。
    代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能够代替真实对  象。同时,代理对象可以在执  行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。
    真实角色:代理角色所代表的真实对象,是我们最终要引用的对象。

    2.参考代码

    1)抽象角色

    public abstract class AbstractSubject {
        public abstract void request();
    }

    2)真实角色

    public class RealAbstract extends AbstractSubject{
        public void request() {
            System.out.println("From Real Subject!");        
        }
    }

    3)代理角色

    public class ProxySubject extends AbstractSubject{
        //代理角色对象内部含有对真实对象的引用
        private RealAbstract realAbstract;
        public void request() {
            //在真实角色操作之前所附加的操作
            preRequest();
            if(null == realAbstract)
            {
                realAbstract = new RealAbstract();
            }
            //真实角色所完成的事情
            realAbstract.request();
            //在真实角色操作之后所附加的操作
            postRequest();
        }
        private void preRequest(){
            System.out.println("Pre Request.");
        }
        private void postRequest(){
            System.out.println("Post Request");
        }
    }

    4)测试类

    public class Client {
        public static void main(String[] args) {
                AbstractSubject subject = new ProxySubject();
                subject.request();        
        }
    }

    问题:

      如果要按照上述的方式(静态代理)使用代理模式,那么真实角色必须是实现已经存在的,并将其作为代理对象的内部属性。

      但是实际使用时,一个真实角色必须对应一个代理角色,但如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以通过Java的动态代理类来解决。

    二、动态代理

      代理类在程序运行前不存在、运行时由程序动态生成的代理方式称为动态代理。

      动态代理动态代理是指客户通过代理类来调用其他对象的方法。

      动态代理使用场合:调试、远程方法调用(RMI)。

    1.动态代理涉及的类

      Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:

      1、Interface InvocationHandler

      该接口中仅定义了一个方法:

      Object invoke(Object proxy, Method method, Object[] args) 

      在实际使用时,第一个参数obj一般是指代理类,method是被代理的方法,如上例中的request(),args为该方法的参数数组(无参时设置为null)。

      这个抽象方法在代理类中动态实现。

      2、Proxy

      该类即为动态代理类,作用类似于上文例子中的ProxySubject,其中主要包含如下内容:

      protected  Proxy(InvocationHandler h): 构造函数,用于给内部的invocation handler赋值。

      static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) : loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

      static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)  :返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类在Subject接口中声明过的方法)。

      3、动态代理类说明

      所谓Dynamic Proxy是这样一种class:

      它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你做实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。在使用动态代理类时,我们必须实现InvocationHandler接口。每一个动态代理类都会有一个与之关联的invocation handler。真正的调用是在invocation handler的invoke()方法里完成的。

    2.动态代理步骤

      1)创建一个实现接口InvocationHandler的类,它必须实现invoke()方法。

      2)创建被代理的类以及接口。

      3)通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)创建一个代理。

      4)通过代理调用方法。

    3.动态代理参考代码

    1)抽象角色

    public abstract class AbstractSubject {
        public abstract void request();
    }

    2)真实角色

    public class RealAbstract extends AbstractSubject{
        public void request() {
            System.out.println("From Real Subject!");        
        }
    }

    3)创建一个实现接口InvocationHandler的类

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;

    public class DynamicSubject implements InvocationHandler {
        //对真实对象的引用
        private Object sub;
        public DynamicSubject(Object obj){
            this.sub = obj;       
        }
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("Before calling: " + method);       
            //通过反射来调用方法
            method.invoke(sub, args);       
            System.out.println("After calling: " + method);
            return null;
        }
    }

    4)测试类

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy;

    public class Client {
        public static void main(String[] args) {
            RealAbstract realSubject = new RealAbstract();
            InvocationHandler handler = new DynamicSubject(realSubject);
            Class<?> classType = handler.getClass();
            // 生成代理
            // 动态生成一个类(实现了指定的接口),生成类的对象,转换成接口类型
            AbstractSubject subject = (AbstractSubject) Proxy.newProxyInstance(

          classType.getClassLoader(),

          realSubject.getClass().getInterfaces(),
                    handler);
            subject.request();
            // 调用方法时,转移给handler接管,由其中的invoke()方法实际完成方法执行
            System.out.println(subject.getClass());// 打印出:class $Proxy0
            // $Proxy0是在运行期间动态生成的一个类
        }
    }

      通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(AbstractSubject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系。

  • 相关阅读:
    前端常用布局
    Vue, React, AngularJS, Angular2 我们对流行JavaScript框架们的选择
    使用vue-cli创建vue项目
    Null component Catalina
    初识MongoDB
    小米平板2 win10 MIUI互刷教程
    tomcat报错
    websocket
    前后端数据交互方法
    二叉排序树
  • 原文地址:https://www.cnblogs.com/liuzhenyou/p/4662397.html
Copyright © 2020-2023  润新知