设计模式-代理模式
代理模式
代理模式是常用的设计模式,他的特征是代理类与委托类具有相同的接口,代理类主要为委托类预处理消息、过滤信息、把消息转发给委托类、以及事后处理消息等。代理类与委类一般会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类对象本身并不提供服务,而是通过调用委托类对象相关的方法来提供服务。
按照代码的创建时期,代理类可分为两种:
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,用来进行扩展类的加载,一般以应jre\lib\ext目录中的类;
- 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()
结果跟静态代理的一样。