代理模式(六)

       在静态代理中对汽车创建了一个时间记录的代理,那么如果我们要对火车、飞机都记录时间呢?这就需要分别对火车、飞机创建代理类。我们需要一个方法来把记录时间这个代理抽离出来,这样当需要对不同的交通工具实现记录时间功能的时候,直接运用这个抽离出的代理,这样可大大减少代码的重用率。这就引出了动态代理。

JDK动态代理简述

       动态代理就是动态产生代理,实现对不同类、不同方法的代理。以下为JDK动态代理的实现机制:

JDK动态代理的实现机制

       如图所示,JDK的动态代理其实就是在代理类ProxySubject与被代理类RealSubject之间加入了一个ProxyHandler类,这个ProxyHandler类实现了InvocationHandler接口,它充当事务处理器,比如类似于上面给汽车计时的日志处理,时间处理等事务都是在这个ProxyHandler类中完成的。

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

Interface InvocationHandler

       InvocationHandler接口源码如下:

1
2
3
4
5
package java.lang.reflect;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

       其中第一个参数proxy代表代理对象;第二个参数method代表被代理的方法;第三个参数代表该方法的参数数组。

Proxy

       该类即为动态代理类。

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

JDK动态代理实现步骤

  1. 创建一个实现InvocationHandler的类,必须实现invoke方法

  2. 创建被代理的类和接口

  3. 调用Proxy的静态方法,创建一个代理类

  4. 通过代理调用方法

代码实现

       下面我们使用动态代理的方式实现静态代理中的例子:

       1.定义TimeHandler.java类实现InvocationHandler接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class TimeHandler implements InvocationHandler {

public TimeHandler(Object target) {
super();
this.target = target;
}

private Object target;

/*
* 参数:
* proxy 被代理对象
* method 被代理对象的方法
* args 方法的参数
*
* 返回值:
* Object 方法的返回值
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
long starttime = System.currentTimeMillis();
System.out.println("汽车开始行驶");
method.invoke(target);
long endtime = System.currentTimeMillis();
System.out.println("汽车停止行驶,行驶时间:"
+ (endtime - starttime) + "毫秒");
return null;
}

}

       2.测试类Test.java的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Test {

/**
* JDK动态代理测试类
*/
public static void main(String[] args) {
Car car = new Car();
InvocationHandler h = new TimeHandler(car);
Class<?> cls = car.getClass();
/**
* loader 类加载器
* interfaces 实现接口
* h InvocationHandler
*/
Moveable m = (Moveable)Proxy.newProxyInstance(cls.getClassLoader(),
cls.getInterfaces(), h);
m.move();
}

}

       测试结果如下:

1
2
3
汽车开始行驶
汽车行驶中
汽车停止行驶,行驶时间:185毫秒

       代码详见 PatternTest

参考资料:
王英豪 与接口相关的设计模式(2):代理模式、标识类型模式及常量接口模式
BestWZR 模式的秘密——代理模式
David 模式的秘密——代理模式

Fork me on GitHub