前言
来源:java核心技术卷1
- 概念:利用代理可以在运行时创建一个实现了一组给定接口的新类。
- 使用时机:只有在编译时不知道要实现接口的确切类型时即才有必要使用
- 问题:要想构造一个实现这些接口的类,就需要使用newInstance()方法或反射找到这个类的构造器,但是不能实例化一个接口,需要在程序处于运行状态时定义一个新类。
- 解决方法:代理类可以在运行时创建一个全新的类,该类能够实现指定的接口,且其具有以下方法:
1)指定接口所需的方法;
2)Object类中的全部方法。
然而,在运行时不能定义这些方法的新代码,而是需要提供一个调用处理器(invocation handler)。调用处理器是实现了InvocationHandler接口的类对象,该接口只有一个方法invoke(Object proxy, Method method, Object[] args)。
java.land.reflect.InvocationHandler 1.3
Object invoke(Object proxy, Method, method, Object[] args)
说明: 定义代理对象调用方法时希望执行的动作。
创建代理对象
要想创建代理对象,需要使用Proxy类的newProxyInstance()方法,该方法有三个参数:
1. 一个类加载器,null表示使用默认加载器。
2. 一个Class对象数组,每个元素都是需要实现的接口。
3. 一个调用处理器。
代理类的特性
- 代理类是在程序运行过程中创建的,一旦被创建就变成了常规类,与虚拟机中的任何其他类没有什么区别。
- 所有代理类都扩展于Proxy类,一个代理类只有一个实例域------调用处理器,它定义在Proxy的超类中,为了履行代理对象的职责,所需要的任何附加数据都必须存储在调用处理器中,如当代理某个接口的对象时,调用处理器类的构造器中会初始化实际实现了这个接口的一个类对象。
- 没有定义代理类的名字,虚拟机中的Proxy类将生成一个以字符串$Proxy开头的类名。
- 对于特定的类加载器和预设的一组接口来说,只能有一个代理类,也就是说,如果使用同一个类加载器和接口数组调用两次newProxyInstance()方法,只能得到同一个类的两个对象,可使用getProxyClass()方法获得这个类。
- 代理类一定是public 和 final。如果代理类实现的所有接口都是public,代理类就不属于某个特定的包,否则,所有非公有的接口必须属于同一个包(代理类可以实现多个接口类),同时代理类也属于这个包。
java.lang.reflect.Proxy 1.3
static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
说明: 返回实现指定接口的代理类。
static Object newProxyInstacne(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler handler)
说明: 构造实现指定接口的代理类的新实例。
使用该对象调用的所有方法会调用给定处理器对象的invoke()方法。
实例:
//定义调用处理器
class MyHander implements InvocationHandler{
//用来接受实现了指定接口的实际对象
private Object target;
public MyHandler(Object t){
target = t;
}
//代理对象调用的所有方法都会跳转到指定的调用处理器中执行invoke()方法
public Object invoke(Object proxy, Method method, Object[]
args) throws Throwable{
//执行操作
...
//调用真实对象对应的方法,args是调用方法需要的参数。
return method.invoke(target, args);
}
}
//使用代理
public class ProxyTest {
public static void main(String[] args){
//用于存放实现了指定接口的代理对象的数组。
Object[] elements = new Object[1000];
// 指定实现的接口是Comparable,Integer类实现了该接口,
// 这里Integer对象指实际类的对象,传入调用处理器的构造器
// 中用于初始化实际的对象。
for(int i - 0; i < elements.length; i++){
//现实中往往是创建一个实现了指定接口的真实对象,传入调用处理
//器中,最终都会在invoke()方法中调用该对象对应的方法
Integer value = i + 1;
InvocationHandler handler = new MyHandler(value);
Object proxy = Proxy.new ProxyInstance(null,
new Class[]{Comparable.class}, handler);
elements[i] = proxy;
}
//随机一个数用于在elements中查找
Integer key = new Random().nextInt(elements.length) +1;
//二分查找,此方法会调用element[i].compareTo(key)方法
//由于elements数组中都是代理对象,因此会调用调用处理器中的invoke()方法
int result = Arrays.binarySearch(elements, key);
if(result >= 0) System.out,println(result);
}
}