读前建议
? ? ? ? 1. 在阅读下面的内容前,笔者假设您已经阅读并理解了《代理》
示例功能
????????该示例程序实现了一个简单地跟踪方法调用过程的功能。具体是使用代理和调用处理器,将代理对象调用的方法和参数打印在控制台。
设计思路
????????主要思路如下:
????????1. 首先构造一个容量为1000的数组用来存放整数。
Object[] elements =new Object[1000];
????????2. 通过代理生成实现了Comparable接口的代理类对象。而在代理类生成代理类对象之后,无论何时用代理类对象(这里是proxy)调用代理类的某个方法,这个方法就回去调用handler(即调用处理器)的invoke方法。
Object proxy = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler);
????????3. 于是我们需要去构造调用处理器(即参数handler)对象。这里通过定义一个TraceHandler包装器类存储了一个包装的对象。
InvocationHandler handler =new TraceHandler(val);
????????另外,TraceHandler实现了InvocationHandler并重写了invoke方法。这是必须的,因为“调用处理器必须给出处理调用的方式(详见《代理》)”。另外,也在TraceHandler中通过构造函数存储了一个包装的对象。
????????4. 此时即可以在invoke方法中给出调用逻辑:打印出被调用方法的名字和参数,然后用包装好的对象作为隐式参数调用这个方法。核心代码如下:
????@Override
????public Objectinvoke(Object proxy, Method method, Object[] args)throws Throwable {
????????System.out.print(target);
? ????? System.out.print("." + method.getName() +"(");
? ????? if(null != args) {
????????????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 method.invoke(target, args);
????}
????????5. 然后就可以随机生成一个1~1000之间的数字,用二分查找法去找到数组elements中与之相等的值并打印在控制台。
int key =new Random().nextInt(elements.length); //a random number
int result = Arrays.binarySearch(elements, key);? ? ? ? //search for the key
if(result >0) System.out.println(elements[result]);? ? //print match if found
这个代理执行是如何实现的?
? ? ? ? 查看binarySearch方法可以看到该方法最终调用了compareTo方法,调用形式是这样的:
? ?if(elements[mid].compareTo(key) < 0)
? ? ? ? ...
? ? else if(elements[mid].compareTo(key) > 0)
? ? ? ? ...
? ? else?
? ? ? ? return mid; //key found
? ? ????最终返回找到元素的下标(不存在会返回一个负数)。由于每一个elements[i]都是一个代理对象(elements[i] = proxy),所以这里相当于用代理类对象调用了该代理对象实现的接口中的方法,即compareTo会去调用TraceHandler中的invoke方法。该invoke方法打印出了方法名和参数,之后用包装好的Integer对象(target)去调用compareTo方法。即:
method.invoke(target, args);
? ? ? ? 下面是某次程序运行的结果:
500.compareTo(869)
750.compareTo(869)
875.compareTo(869)
812.compareTo(869)
843.compareTo(869)
859.compareTo(869)
867.compareTo(869)
871.compareTo(869)
869.compareTo(869)
869.toString()
869
? ? ? ? 可以看出,二分查找法查找关键字的过程,即每一步都将查找区域减半。注意:即使不属于Comparable,但是toString方法也会被代理执行。因为所有代理类都覆盖了Object类中的toString、equals和hashCode方法。这些方法和所有的代理方法一样,当调用这些方法时,实质是去调用了调用处理器的invoke方法。Object的其他方法(如:clone、getClass)没有被重新定义。详见:代理类的特性。
代码下载
? ? ? ? 完整示例代码