(1)何时使用代理
假如有一个表示接口的Class对象(有可能只包含一个接口),它的确切类型在编译时无法知道。这确实有点难受。想要构造一个实现这些接口的类,就需要使用newInstance方法或反射找出这个类的构造器。但是,不能实例化一个接口,需要在程序处于运行状态时定义一个新类。
为了解决这个问题,有些程序姜维生成代码;将这些代码放置在一个文件中;调用编译器;然后再加载结果类文件。很自然,这样做的速度会比较慢,而且需要将编译器与程序放在一起。而代理机制则是一种更好的解决方案。代理类可以在运行时创建全新的类。这样代理类能够实现指定的接口。尤其是,它具体有下列方法:
*指定接口所需要的全部方法
*Object类中的全部方法,如,toString、equals等。
然而,不能在运行时定义这些方法的新代码。而是要提供一个调用处理器。调用处理器是实现了InvocationHandler
接口的类对象。在这个接口中只有一个方法:Object invoke(Object proxy, Method method, Object[] args)
无论何时调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数。调用处理器必须给出处理调用的方式。
(2)创建代理对象
要想创建一个代理对象,需要使用Proxy类的newProxyInstance方法。这个方法有三个参数:
*一个类加载器,作为Java安全模型的一部分,对于系统类和 从因特网上下载下来的类,可以使用不同的类加载器。
*一个Class对象数组,每个元素都是需要实现的接口。
*一个调用处理器
还有两个问题需要解决。如何定义一个处理器?能够用结果代理对象做些什么?使用代理可能处于很多原因:
*路由对远程服务器的方法调用。
*在程序运行期间,将用户接口事件与动作关联起来。
*为调试,跟踪方法调用。
class TraceHandler implements InvocationHandler {
private Object target;
public TraceHandler(Object t) {
this.target = t;
}
public Object invoke(Object proxy, Method m, Object[] args) {
return m.invoke(target, args)
}
}
//下面说明如何构造用于跟踪方法调用的代理对象
Object value = ...;
InvocationHandler handler = new TraceHandler(value);
Class[] interface = new Class[]{ Comparable.class };
Object proxy = Proxy.newProxyInstance(null, interfaces, handler);
(8)代理类的特性
代理类是在程序运行过程中创建的。然而,一旦被创建,就变成了常规类,与虚拟机中的任何其他类没有什么区别。
所有的代理类都扩展于Proxy类。一个代理类只有一个实例域–调用处理器,它定义在Proxy的超类中。为了履行代理对象的职责,所需要的任何附加数据都必须存储在调用处理器中。