Java
SDK
中提供了在运行期生成动态代理类的机制. 被代理类定义接口规范, 代理类通过反射可以执行被代理类的接口规范中的函数.使用动态代理机制时, 涉及到几个不同的角色:被代理类, 代理类,InvocationHandler
类以及Proxy
类. 他们的功能说明如下:
InvocationHandler
类继承InvocationHandler接口并实现其invoke
方法. 该类是代理类和被代理类的联系人, 当代理类执行相关接口服务时, 将调用到该类的invoke方法,因此可以在invoke方法里面监控或者统计相关服务调用. 比如,可以简单的输出log
来检查当前执行的服务.Proxy
类是Java SDK中的类,该类通过调用SDK其他类负责创建代理类实例.因此程序执行时, 执行的是代理类的实例,最终调用到被代理类的相关服务.其流程如下图所示:
下面用一个简单的例子说明动态代理的使用.
接口类:
public interface FooInterface {
public void sayHi();
}
被代理类, 继承接口规范:
public class FooImplWithInterface implements FooInterface {
public void sayHi() {
System.out.println("I'm FooImplWithInterface.");
}
}
InvocationHandler
类:
public class FooInvocationHandler implements InvocationHandler {
private Object foo;
public Object getProxyObject(Object foo) {
this.foo = foo;
try {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), foo.getClass().getInterfaces(), this);
} catch (IllegalArgumentException ignore) {
}
return null;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invoke foo's method.");
final Object result = method.invoke(foo, args);
System.out.println("after invoke foo's method.");
return result;
}
}
运行入口类:
public class FooMain {
public static void main(String[] args) {
final FooInvocationHandler ih = new FooInvocationHandler();
final FooInterface fi = (FooInterface) ih.getProxyObject(new FooImplWithInterface());
System.out.println("proxy class:" + fi.getClass());
fi.sayHi();
}
}
运行程序将输出log
:
proxy class:class com.sun.proxy.$Proxy0
before invoke foo's method.
I'm FooImplWithInterface.
after invoke foo's method.
其中有几个细节需要注意的是:
FooInvocationHandler
的getProxyObject
函数中, 通过调用类Proxy
的newProxyInstance
来构造一个代理类实例,从log输出可以看出,代理类为com.sun.proxy.$Proxy0
, 是Java在运行期动态生成的, 代理类名字以$Proxy
开始,后面连接一个数字.该代理类将继承Proxy
类并实现被代理类实现的接口. 即代理类的签名为:public final class $Proxy0 extends Proxy implements FooInterface
.Proxy
类的newProxyInstance
函数,传入的三个参数分别为, 用来定义代理类的class loader;被代理类实现的接口列表;以及InvocationHandler
类实例.因此被代理类必须实现接口(一个或者多个), 否则newProxyInstance
构造的Object
类实例无法造型(cast
)为接口,也就无法实现代理调用的功能.FooInvocationHandler
实现的invoke
函数中, 将继续通过反射调用被代理类的相应函数, invoke函数的第一个参数为代理类实例, 因此, 不应该用该参数去执行反射调用,否则将导致死循环. 而是要通过被代理类的实例来执行反射调用.相关源码实现
Java SDK的Proxy类的newProxyInstance
在运行期构造代理类, 其实现为(Proxy.java
):
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
该函数的主要逻辑包括,Class> cl = getProxyClass0(loader, intfs);
获得代理类. 第一次获取代理类时, Java通过ProxyClassFactory
类动态生成该类并装载, 然后将其保存在内存中. final Constructor> cons = cl.getConstructor(constructorParams);
将返回代理类的Constructor
对象, 注意所有代理类的构造器都有相同的参数签名:constructorParams
,其定义为(Proxy.java
):
/** parameter types of a proxy class constructor */
private static final Class>[] constructorParams =
{ InvocationHandler.class };
即代理类的构造器都需要传入一个InvocationHandler
实例.接着调用return cons.newInstance(new Object[]{h});
构造代理类实例并返回.
动态生成的代理类的构造函数类似为:
public $ProxyXXX(InvocationHandler var1) {
super(var1);
}
因为代理类继承Proxy
类, 所以将执行Proxy的相应构造器函数(Proxy.java
):
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
即在Proxy的成员变量h
中保存InvocationHandler
实例.
代理类因为要实现被代理类的接口规范, 对于本例, 代理类需要实现sayHi
函数, 其主要逻辑为:
super.h.invoke(this, method, args);
其中method为相应函数的Method
实例, args为其参数(可能为空).
有了上面的介绍, 对于代理类调用流程就比较清晰了, 当代理类执行fi.sayHi();
是, 其内部调用super.h.invoke(this, method, args);
会继续调用父类Proxy
的成员变量h
的invoke
函数, h
成员变量是代理类构造时传入的:cons.newInstance(new Object[]{h}),
也就是newProxyInstance
函数的第三个参数,即FooInvocationHandler
实例.所以会执行FooInvocationHandler的invoke函数,在该函数中可以进行一些监控和统计,然后调用被代理类的相应函数.