【0】README
0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java代理机制 的基础知识;
【1】代理相关
1.1)利用代理可以在运行时创建一个实现了一组给定接口的新类, 这种功能只有在 编译时无法确定需要实现哪个接口时 才有必要使用;
1.2)假设有一个表示接口的 Class对象, 它的确切类型在编译时无法知道。 要想构造一个实现这些接口的类, 就需要使用 newInstance 方法或反射找出这个类的构造器;但是不能实例化一个接口, 需要在程序处于运行状态时定义一个新类;
1.3)解决问题的方法(代理机制): 代理机制是一种更好的解决方法。代理类可以在运行时创建全新的类, 这样代理类能够实现指定的接口,尤其是, 它具有以下方法:
- 1.3.1)指定接口所需要的全部方法;
- 1.3.2) Object 类中的全部方法, 例如 , toString, equals等;
- 1.3.3)然而,不能在运行时定义这些方法的新代码。而是要提供一个 调用处理器(InvocationHandler), 该处理器是实现了 InvocationHandler 接口的类对象。在这个接口中只有一个方法: Object invoke(Object proxy, Method method, Object[] args) ; 要知道, 无论何时调用代理对象的方法, 调用处理器的invoke 方法都会被调用, 并向其传递Method对象和原始的调用参数;
1.4)要想创建一个代理对象, 需要使用Proxy类的 newProxyInstance 方法, 该方法有3个参数:
- 1.4.1)一个类加载器:
- 1.4.2)一个Class 对象数组: 每个元素都是需要实现的接口;
- 1.4.3)一个调用处理器:
class TraceHandler implements InvocationHandler
InvocationHandler handler = new TraceHandler(value);
Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class } , handler);
1.5)但是,还有两个问题需要解决:
当然了,这两个问题的答案取决于打算使用代理机制解决什么问题。使用代理可能出于很多原因,如:
- ex1)路由对远程服务费去的方法调用;
- ex2)在程序运行期间, 将用户接口事件与动作关联起来;
- ex3)为调试, 跟踪方法调用;
【2】看个荔枝(跟踪方法调用):
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;
InvocationHandler handler = new TraceHandler(value);
Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class } , 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]);
}
}
class TraceHandler implements InvocationHandler
{
private Object target;
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 m.invoke(target, args);
}
}
2.1)打印结果:
【2】代理类的特性
2.1)需要记住: 代理类是在程序运行过程中创建的, 然而, 一旦被创建, 就变成了常规类,与虚拟机中的任何其他类没有区别;
2.2)所有的代理类都扩展于 Proxy类。一个代理类只有一个实例域——调用处理器, 它定义在 Proxy的超类中, 为了履行代理对象的职责, 所需要的任何附加数据都必须存储在调用处理器中;
2.3)所有的代理类都覆盖了 Object类中的方法 toString、equals 和 hashCode 方法;这些方法仅仅 调用了 调用处理器 的invoke 方法;Object类中的其他方法没有被重新定义;
2.4)没有定义代理类的名字的话, Sun 虚拟机中的Proxy类将生成一个 以 字符串 $Proxy 开头的类名;
2.5)对于特定的类加载器和预设的一组接口来说, 只能有一个代理类;也就是说, 如果使用 同一个类加载器和接口数组调用两次 newProxyInstance 方法的话, 那么只能够得到同一个类的两个对象, 也可以利用 getProxyClass 方法获得这个类:
Class proxyClass = Proxy.getProxyClass(null, interfaces);
2.6)代理类一定是 public 和 final, 如果代理类实现的所有接口都是 public, 代理类就不属于某个特定的包;否则,所有非公有的接口都必须属于同一个包;
2.7)可以调用 Proxy类中的 isProxyClass 方法 检测一个特定的 Class对象是否代表一个代理类;
[API] java.lang,reflect.InvocationHandler 1.3
Object invoke(Object proxy, Method method, Object[] args):返回实现指定接口的代理类;
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler handler) : 构造一个实现指定接口的代理类的实例, 所有方法都将调用给定处理器对象的invoke 方法;
static boolean isProxyClass(Class c) : 如果 c 是一个代理类返回 true;