利用代理可以在运行时创建一个实现了一组接口的新类。
这种功能只有在编译时无法确定需要实现哪个接口时才使用。对于应用程序设计人员来说,遇到这种情况的机会很少。这是一种高级技术,对于系统程序设计人员来说,代理带来的灵活性十分重要。
代理如何工作的
代理类可以在运行时创建全新的类。在运行时直接生成类,即Class类的一个实例。而非生成一个类的源码,然后经过编译后再生成类的字节码文件,再经过JVM加载、生成类。因此运行时生成类性能很快。
代理类能够实现指定的接口的所有需要的方法,并且也实现了Object类的toString、equals方法。
代理类并非是通过自己全新的定义来实现这些接口方法的,而是通过一个调用处理器即InvocationHandler接口的一个实例对象来实现的。
InvocationHandler
接口中只有一个方法:
/*
@param poxy 代理类
@param method 当代理类工作时,调用被代理对象的那个方法;即代理类实现的接口方法。
@param args 被代理的方法的参数
*/
Object invoke(Object proxy, Method method, Object[] args)
代理类的代理作用即代理要做的工作就要写在这个invoke方法中,再method.invoke(args);方法前后做一些必要的工作。
无论何时调用代理对象的方法,invoke方法都会被调用,invoke方法中必须给出处理调用的代码。
创建代理对象
需要用JDK的Proxy类的newProxyInstance方法:
static Object newProxyInstance(ClassLoader loader, Class<?> interfaces, InvocationHandler h);
在运行时动态生成类,首先要指定类加载器 loader,然后指明对哪些接口——interfaces生成代理类,最后确定生成的代理类最终是调用哪个被代理类的哪些方法——invocationHandler实例——一个调用处理器。
使用代理类的可能场景
- 路由对远程服务的方法调用
- 在程序运行期间,将用户接口事件与动作关联起来
- 为了调试,跟踪方法调用
举个例子
追踪Arrays.binarySearch方法的查找过程:输入查找一个给定的key时,经过了几次比较,每次是同哪个数比较的。
分析:binarySearch方法是JDK的方法,它调用comparable接口的compareTo方法来做比较和查找,我们无法在这个方法里插入代码,来做输出,但是代理可以做到。
代理就是在执行本要做的事情的前后,再做一些其它的事情,来实现代理的功能。
import java.lang.reflect.*;
import java.util.*;
class ComparableProxy implements InvocationHandler
{
private Object target;
public IntegerProxy(Object target){
this.target = target;
}
public Object invoke(Object p,Method m,Object [] args) throws Throwable{
// 在做比较工作前,输入比较对象信息
StringBuilder sb = new StringBuilder(this.toString()).append("],[target:").append(target.toString()).append("],[args:").append(Arrays.toString(args));
System.out.println(sb.toString());
// 调用实际工作的比较方法
return m.invoke(target,args);
}
}
import static java.lang.System.out;
import java.util.*;
import java.lang.reflect.*;
public class Test{
public static void main(String[] args){
Object [] proxys = new Object[1000];
for(int i=0;i<1000;i++){
proxys[i] = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, new ComparableProxy(i+1));
}
int key = new Random().nextInt(1000) + 1;
out.println("key:"+key);
int result = Arrays.binarySearch(proxys, key);
out.println("result:"+result);
}
}
/*
key:780
[target:500],[args:[780]
[target:750],[args:[780]
[target:875],[args:[780]
[target:812],[args:[780]
[target:781],[args:[780]
[target:765],[args:[780]
[target:773],[args:[780]
[target:777],[args:[780]
[target:779],[args:[780]
[target:780],[args:[780]
result:779
*/
代理类是在程序运行过程中创建的。 一旦被创建, 就变成了常规类, 与虚拟机中的任何其他类没有什么区别。
所有的代理类都扩展于 Proxy 类。一个代理类只有一个实例域—调用处理器,它定义超类 Proxy 中。 为了履行代理对象的职责, 所需要的任何附加数据都必须存储在调用处理器中。 例如, 在上例中, 代理 Comparable 对象时, ComparableProxy 包装了实际的对象。