设计模式-代理模式

代理模式

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

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

  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()

结果跟静态代理的一样。

原文地址:https://www.cnblogs.com/johnnyzhao/p/10400747.html