上一章讲到Java在J2SE1.3之后引入了java.lang.reflect.Proxy类。通过包名和类名我们可以判断出
接下来,我们要分析一下,Proxy类如何基于类加载器、接口数组以及MyInvocationHandler来构建一个代理类?代理对象如果反射调用真实对象的方法?
这是一个基于反射机制原理实现的具有代理功能的类。我们先来看一下这个类的大概结构:
public class Proxy implements java.io.Serializable {
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
……
protected InvocationHandler h;
//Proxy的构造函数,接收一个非空InvocationHandler对象
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
//通过真实对象的类加载器以及真实类所实现的接口,来生成一个代理类,并返回代理类的类对象
public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces){
return getProxyClass0(loader, intfs);
}
//生成代理对象的静态方法,这个代理对象extends Proxy,并且implements真实类所实现的接口,
//传入的InvocationHandler对象,会在代理对象调用方法时,通过这个InvocationHandler来调用自身的invoke方法,
//从而进一步反射调用真实对象的方法
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,
InvocationHandler h){
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
return cons.newInstance(new Object[]{h});
}
}
同静态代理一样,我们需要为传入的真实对象生成代理对象。JDK源码中给出了两种生成代理对象的方法:
1. InvocationHandler handler = new MyInvocationHandler();
Class<?> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
Foo f = (Foo) proxyClass.getConstructor(InvocationHandler.class).
newInstance(handler);
2 Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class<?>[] { Foo.class },handler);
从源码中可以看出,第二种方法是第一种方法的简写,将生成代理类=》获取构造方法=》生成代理对象整合到了一起。
InvocationHandler 是一个接口,只有一个invoke方法。下面我们先来实现一个这个接口:
public class MyInvocationHandler implements InvocationHandler {
private Object obj;
public MyInvocationHandler(Object obj){
this.obj = obj;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在此添加翻墙操作*******
System.out.println("已成功翻墙");
Object ret = method.invoke(this.obj, args);
// 在此解除翻墙操作*******
System.out.println("已成功解除翻墙");
return ret;
}
}
接下来我们修改一下TestBrowser测试:
public class TestBrowser {
public static void main(String[] args) {
IBrowser realBrowser = new IBrowserImpl();
// 传入真实对象到MyInvocationHandler构造函数,handler对象在用反射调用时使用的就是这个对象
InvocationHandler handler = new MyInvocationHandler(realBrowser);
IBrowser proxyBrowser = (IBrowser)Proxy.newProxyInstance(realBrowser.getClass().getClassLoader(),
realBrowser.getClass().getInterfaces(), handler);
proxyBrowser.browserURL("www.google.com.hk");
}
}
这里我们将真实对象传入了MyInvocationHandler,并将生成的handler传给了Proxy的构造方法,Proxy利用传入的类加载器、接口数组以及handler对象,构建除了一个新的代理类(如何构建在后面讲述),并生成了一个代理对象。这个代理对象调用browserURL方法,其实是调用了MyInvocationHandler的invoke方法,通过这个invoke方法,再反射调用realBrowser的browserURL方法。
运行测试一下,输出了和静态代理同样的结果:
已成功翻墙
您现在可以浏览www.google.com.hk网页了
已成功解除翻墙
现在我们回过头来看看我们的
TestBrowser测试程序,用户其实不关心代理对象是如何产生及运作的,所以我们暴露给用户的接口必须简洁明了,因此我们优化一下MyInvocationHandler:
public class MyInvocationHandler implements InvocationHandler {
private Object obj;
public Object bind(Object realObj){
this.obj = realObj;
return Proxy.newProxyInstance(
realObj.getClass().getClassLoader(),
realObj.getClass().getInterfaces(),
this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 在此添加翻墙操作*******
System.out.println("已成功翻墙");
Object ret = method.invoke(this.obj, args);
// 在此解除翻墙操作*******
System.out.println("已成功解除翻墙");
return ret;
}
}
TestBrowser:
public class TestBrowser {
public static void main(String[] args) {
IBrowser realBrowser = new IBrowserImpl();
IBrowser proxyBrowser = (IBrowser)new MyInvocationHandler().bind(realBrowser);
proxyBrowser.browserURL("www.google.com.hk");
}
}
现在讲生成代理对象的代码移到了MyInvocationHandler,暴露给用户一个bind方法,简单而形象,将真实对象绑定到代理对象上。
接下来,我们要分析一下,Proxy类如何基于类加载器、接口数组以及MyInvocationHandler来构建一个代理类?代理对象如果反射调用真实对象的方法?