代理
利用代理可以在运行时创建一个实现了一组给定接口的新类。这种功能只有在编译时无法确定需要实现哪个接口时才有必要使用。
1、何时使用代理
对于一个表示接口的Class对象,它的确切类型在编译时无法知道。利用代理可以在运行时创建全新的类,该类能够实现指定的接口。它具有以下方法:
- 指定接口所需要的全部方法。
- Object类中的全部方法,如toString、equals等。
创建代理类时,需要提供一个调用处理器(invocation handler),它是实现了InvocationHandler接口的类对象。在该接口中只有一个方法:
Object invoke(Object proxy,Method method,Object[] args)
无何时论调用代理对象的方法,调用处理器的invoke方法都会被调用,并向其传递Method对象和原始的调用参数。调用处理器的invoke方法中必须给出处理调用的方式。
2、创建代理对象
创建代理对象时,需要使用Proxy类的newProxyInstance方法,该方法有三个参数:
- 一个类加载器
- 一个Class对象数组,每个元素都是需要实现的接口
- 一个调用处理器
public class ProxyTest {
public static void main(String[] args) {
Object[] elements = new Object[1000];
for (int i = 0;i < elements.length;i++) {
Integer value = i+1;
InvocationHandler handler = new TraceHandler(value);
Object proxy = Proxy.newProxyInatance(null,new Class[] {Comparable.class},handler);
elements[i] = proxy;
}
Integer key = new Random().nextInt(elements.length)+1;
int result = Arrays.binarySearch(elements,key); // 按这种方式调用:if (elements[i].compareTo(key) < 0) …
if(result >= 0) System.out.println(elements[result]);
}
}
class TraceHandler implements InvocationHandler {
private Object target;
public TraceHandler(Object t) {
target = t;
}
public Object invoke(Object proxy,Method m,Object[] args) {
System.out.print(target);
System.out.print("." + m.getName() + "(");
if (args != null) {
for(int i = 0;i < args.length;i++) {
System.out.print(args[i]);
if (i < args.length-1) System.out.print(", ");
}
}
System.out.println(")");
return m.invoke(target,args);
}
}
3、代理类特性
- 所有的代理类都扩展于Proxy类。一个代理类只有一个实例域——调用处理器,它定义在Proxy的超类中。为完成代理职责,所需要的任何附加数据都必须存储在调用处理器中。
- 所有的代理类都覆盖了Object类中的方法toString、equals和hashCode。如同所有的代理方法一样,这些方法仅仅调用了调用处理器的invoke。Object类中的其他方法(如clone和getClass)没有被重新定义。
- 没有定义代理类的名字,Sun虚拟机的Proxy类将生成一个以字符串$Proxy开头的类名。
- 对于特定的类加载器和预设的一组接口来说,只能有一个代理类。可用getProxyClass方法获得这个类:
Class proxyClass = Proxy.getProxyClass(null,interfaces);
static boolean isProxyClass(Class<?> c1)
可检测从c1是否为一个代理类。
参考:
《Java核心技术 卷I》