• 设计模式-代理模式


    代理模式

    代理模式是常用的设计模式,他的特征是代理类与委托类具有相同的接口,代理类主要为委托类预处理消息、过滤信息、把消息转发给委托类、以及事后处理消息等。代理类与委类一般会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类对象本身并不提供服务,而是通过调用委托类对象相关的方法来提供服务。

      按照代码的创建时期,代理类可分为两种:

      a)静态代理:通过代码工具或手写生成源代码,再对其编译。在程序运行前,代理类相关.class就已经存在;

      b)动态代理:在程序运行时,通过反射机制动态创建而成;

     

      静态代理:

      静态代理其实就是按照代理模式,实现了代理类与委托类之间的调用方式,可以看作是代理模式的写法;

      代理模式有三个角色:

    • 抽象代理角色:声明了目标对象与代理对象的共同接口,这样可以在任何可以使用目标对象的地方都可以使用代理对象。
    • 目标对象角色:定义了代理对象所对表的目标对象。
    • 代理对象角色:代理对象的内部含有目标对象的引用,从而可以在任何时候使用目标对象;代理对象提供一个与目标对象相同的接口,以便可以在任何时候替代目标对象。代理对象通常在客户端调用传递给目标对象之前或之后,执行某个操作,而不是单纯地将调用传递给目标对象。  

      源代码 

      

    /***
     * 抽象对象
     */
    interface AbstractObject {
        fun request()
    }
    /**
     * 委托类(目标类)
     * */
    class RealObject :AbstractObject {
    
        override fun request() {
            println("http request")
        }
    }
    /**
     * 代理类
     * */
    class ProxyObject : AbstractObject {
    
      //委托类 private var realObject: RealObject? = null constructor(realObject_: RealObject) { this.realObject = realObject_ } override fun request() { println("调用目标对象之前的一些操作") realObject?.request() println("调用目标对象之后的一些操作") } }

    调用:

    class Main {
        companion object {
            @JvmStatic
            fun main(args: Array<String>) {
                val realObject = RealObject()
                val proxyObject = ProxyObject(realObject)
                proxyObject.request()
            }
        }
    }

    结果:

    调用目标对象之前的一些操作
    http request
    调用目标对象之后的一些操作

    动态代理: 

    动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。

    代理类和委托类的关系是在程序运行时确定。

    通俗来说:比如登录之前需要判断用户信息是否完整、请求之前需要配置请求数据等等,动态代理就是来处理这样的需求,让用户只需要关心事件处理。

    我们先看看与动态代理紧密关联的java api:

    • java.lang.reflect.Proxy

      动态代理的加载器;

    • java.lang.reflect.InvocationHandler

      动态代理类处理方法主要是依赖InvocationHandler接口;

    • java.lang.ClassLoader

    Proxy的代码:

    //ClassLoader, 指被代理的对象
    //Class[] interfaces,要调用的方法
    //InvocationHandler h,方法调用时所需的参数
    public
    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { ... }

    该类的newProxyInstance()方法需要一个ClassLoader类的实例,ClassLoader实际上对应的是类加载器,java中有以下三种类加载器:

    • Booststrap ClassLoader,此加载器采用C++编写,一般开发中是看不到的;
    • Extendsion ClassLoader,用来进行扩展类的加载,一般以应jrelibext目录中的类;
    • AppClassLoader,(default)加截classpath指定的类,是最常使用的一种加载器;

    InvocationHandler的代码:

    //Object proxy,指被代理的代象
    //Method method,要调用的方法
    //Object[] args,方法调用时所需的参数
    public
    interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }

    invoke方法是动态处理类的主要方法;

    动态代理的实现步骤

    • 实现InvocationHandler接口创建自已的调用处理口
    • 给Proxy类提供ClassLoader和代理接口类型数组创建动态代理类
    • 以调用处理器类型为参数,利用反射机制得到动态代理类的构造函数
    • 以调用处理器对象为参数,利用动态代理类的构造函数创建动态代理类对象

    源代码

    class HttpRequestJDKProxy : InvocationHandler {
    
        private var target: Any? = null
    
        fun bind(target_: Any):Any? {
            this.target = target_
            //取得代理对象
            return Proxy.newProxyInstance(
                target!!::class.java.classLoader,
                target!!::class.java.interfaces,
                this
            )
        }
    
        override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
            println("调用目标对象之前的一些操作")
            //利用反射机制将请求分派给委托类处理。
            //Method的invoke返回Object对象作为方法执行结果。
         val result = method?.invoke(target, *(args ?: arrayOfNulls<Any>(0)))
            println("调用目标对象之后的一些操作")
            return result
        }
    }

    笔者用kotlin语言来编写例子遇到了以下的问题:

    反射api中Method的invoke方法中第二个参数接收可变长参数,在java中允许数组赋值给可变长参数Object... args,Kotlin中,数组是array,可变长参数类型是vararg,类型不一致,运行时会报错,所以在kotlin中,可以使用*符号放在args前面,把它变成可变长参数。参考

    运行

    val jdkProxy = HttpRequestJDKProxy()
    val realObject = RealObject()
    val dynamicProxyObject = jdkProxy.bind(realObject) as AbstractObject
    dynamicProxyObject.request()

    结果跟静态代理的一样。

  • 相关阅读:
    Ubuntu_14.04安装docker
    CentOS配置java运行环境
    github上传自己的开源代码
    eclipse使用maven插件创建web项目
    jar包解压后,修改完配置文件,再还原成jar包
    Python学习的几本建议书籍
    流批
    函数
    程序
    习 题
  • 原文地址:https://www.cnblogs.com/johnnyzhao/p/10400747.html
Copyright © 2020-2023  润新知