介绍
利用 Java 反射机制你可以在运行期动态的创建接口的实现。
动态的代理的用途十分广泛,比如数据库连接和事物管理(transaction management)还有单元测试时用到的动态 mock 对象以及 AOP 中的方法拦截功能等等都使用到了动态代理。
创建代理
你可以通过使用 Proxy.newProxyInstance() 方法创建动态代理。 newProxyInstance() 方法有三个参数:
- 类加载器(ClassLoader)用来加载动态代理类。
- 一个要实现的接口的数组。
- 一个 InvocationHandler 把所有方法的调用都转到代理上。
如下例:
InvocationHandler handler = new MyInvocationHandler();
MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
MyInterface.class.getClassLoader(),
new Class[] { MyInterface.class },
handler);
在执行完这段代码之后,变量 proxy 包含一个 MyInterface 接口的的动态实现。所有对 proxy 的调用都被转向到实现了 InvocationHandler 接口的 handler 上。
InvocationHandler 接口
前面提到了当你调用 Proxy.newProxyInstance() 方法时,你必须要传入一个 InvocationHandler 接口的实现。所有对动态代理对象的方法调用都会被转向到 InvocationHandler 接口的实现上。
下面是 InvocationHandler 接口的定义:
public interface InvocationHandler{
Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
下面是它的实现类的定义:
public class MyInvocationHandler implements InvocationHandler{
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
//do something "dynamic"
}
}
传入 invoke() 方法中的 proxy 参数是实现要代理接口的动态代理对象。通常你是不需要他的。
invoke() 方法中的 Method 对象参数代表了被动态代理的接口中要调用的方法,从这个 method 对象中你可以获取到这个方法名字,方法的参数,参数类型等等信息。
Object 数组参数包含了被动态代理的方法需要的方法参数。
注意
原生数据类型(如 int,long 等等)方法参数传入等价的包装对象(如 Integer, Long 等等)。
类似 AOP 的方法拦截器
Spring 框架可以拦截指定 bean 的方法调用,你只需提供这个 bean 继承的接口。Spring 使用动态代理来包装 bean。所有对 bean 中方法的调用都会被代理拦截。代理可以判断在调用实际方法之前是否需要调用其他方法或者调用其他对象的方法,还可以在 bean 的方法调用完毕之后再调用其他的代理方法。