在Java中利用代理(Proxy)可以在运行时创建一个实现了一组给定接口的新类。
在系统程序设计中,有时需要面对无法确定接口,却需要构造对象的情况。以前为了解决此问题,有些程序根据动态确定的接口,生成Java类文件,然后调用类加载器构造该对象,然后使用,这样一来无可避免性能问题。通过代理类,能够在不额外创建Java文件的情况下构造对象及调用该对象方法。
使用代理的理由有很多,其中就有如下的情况:
1.路由对远程服务器的方法调用
2.在程序运行期间,将用户接口事件与行动关联起来
3.调试时跟踪方法调用
以下举出一例,使用代理和调用处理器跟踪方法调用
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Random; public class ProxyTest { public static void main(String[] args) { Object[] elements = new Object[1000]; // fill elements with proxies for the integers 1 1000 for (int i = 0; i < elements.length; i++) { Integer value = i + 1; Class[] interfaces = value.getClass().getInterfaces(); InvocationHandler handler = new TraceHandler(value); Object proxy = Proxy.newProxyInstance(null, interfaces, handler); elements[i] = proxy; } // construct a random integer Integer key = new Random().nextInt(elements.length) + 1; // search for the key int result = Arrays.binarySearch(elements, key); // print match if found if (result >= 0) System.out.println(elements[result]); } } /** An invocation handler that prints out the method name and parameters, then invokes the original method */ class TraceHandler implements InvocationHandler { /** Constructs a TraceHandler @param t the implicit parameter of the method call */ public TraceHandler(Object t) { target = t; } public Object invoke(Object proxy, Method m, Object[] args) throws Throwable//此方法在代理类中的方法被调用时均会被调用 { // print implicit argument System.out.print(target); // print method name System.out.print("." + m.getName() + "("); // print explicit arguments 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(")"); // invoke actual method // return new Integer("123"); return m.invoke(target, args); } private Object target; }
当调用代理类的方法时,调用实现InvocationHandler接口的方法invoke,此时可以针对传入参数Method的不同,定义不同的方法体(操作)
Proxy类的特性:
1.代理(Proxy)类只有一个实例变量,即调用处理器(InvocationHandler)
2.代理类需要的额外数据都必须存储在调用处理器中
3.代理类一定是public和final.
4.如果代理类实现的所有接口都是public,则代理类就不属于某个特定的包;否则所有的非公有接口都必须属于同一个包,代理类也属于这个包(此设定的目的是确定代理类的所属包)