版本一
试着只去看API,根据自己的理解,写出了Java动态代理的第一个版本
publicclass DynamicProxyTest { publicstaticinterface IWorker { public String work(); }
publicstaticclass Worker implements IWorker{ public String work() { System.out.println("working..."); return"hello, work"; } }
publicstaticclass Invocator implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before work..."); String s = (String)method.invoke(proxy); System.out.println("after work..."); returns; } } publicstaticvoid main(String[] args) { InvocationHandler invocator = new Invocator(); IWorker proxyWorker = (IWorker)Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(), new Class[]{IWorker.class}, invocator); System.out.println(proxyWorker.work()); } } |
在不知道具体怎么使用动态代理的情况下,主要围绕Proxy.newProxyInstance这个方法猜测用法的。这个方法接收三个参数,如下:
ClassLoader loader:这个好办,把当前类加载器传入即可。
Class<?>[] interfaces:生成的代理类有哪些方法,是通过传入哪些接口类决定的(这也表明了java动态代理是基于接口的);这个参数也没有疑问,直接把我们要代理的接口传入即可:new Class[]{IWorker.class}。
InvocationHandler h:实际上在实现InvocationHandler的时候不太清楚怎么实现,主要是不清楚接口中的Object proxy代表什么,于是写成了下面的样子;直接new了一个该实现类当参数传入了。
publicstaticclass Invocator implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before work..."); String s = (String)method.invoke(proxy); System.out.println("after work..."); returns; } } |
不出所料,挂掉了,而且错误信息根本停不下来啊。
想了一下,最有可能出问题的地方是:不太清楚InvocationHandler接口中接口方法invoke(Object proxy, Method method, Object[] args)中,参数proxy究竟是一个什么对象呢?
看Proxy.newProxyInstance的源代码(SUN JDK):
第一步:
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, interfaces);
生成代理类的字节码,具体怎么生成的先不管。
第二步:
/** parameter types of a proxy class constructor */
privatefinalstatic Class[] constructorParams =
{ InvocationHandler.class };
final Constructor<?> cons = cl.getConstructor(constructorParams);
获取代理类的构造函数类(构造函数在JVM中使用Constructor类表示)。
第三部:
cons.newInstance(new Object[] {h} );
构造函数+构造函数参数生成代理类实例,一个我们需要的代理类就生成的。
现在关于生成的代理类,有以下几点可以确定:
1、代理类以一个InvocationHander实例为参数,该实例是通过Proxy.newProxyInstance传入的。
2、代理类具有它所代理的接口的所有方法,比如,Iworker的代理类,就一定会有一个work()方法,因为我们将来还要通过代理类调用work()方法的。
所以,IWorker生成的代理类大体长这样:
/** * 模仿JDK动态生成的代理类 * Java为IWorker生成的代理类,应该类似我们这里实现的该类。 * 只不过JDK使用字节码技术动态生成该类,因此灵活性更高。 * @author Grucee */ publicstaticclass ImitationProxy implements IWorker{ private InvocationHandler handler; public ImitationProxy(InvocationHandler handler) { this.handler = handler; }
@Override public String work() { returnnull; }
} |
那么它的work方法又是怎么实现呢,结合InvocationHandler的接口,该方法应该是知己调用InvocationHandler的invoke方法:
public String work() { return handler.invoke(this, method, args); } |
上面的代码是编译不通过的,因为我们看似无法获取到method,args参数。其实不然,代理类怎么知道自己是有哪些方法要实现的,肯定是通过Proxy.newProxyInstance第二个参数(接口类数组),然后通过Class:getDeclaredMethods()获取到有哪些方法要实现,所以它在生成代理类的work方法的时候,肯定就知道它是Iworker.class. getDeclaredMethods[]中的work方法了;当然也就知道这个方法有哪些参数了。
至此,我们终于搞清楚了InvocationHandler:invoke(Object proxy, Method method, Object[] args)中,参数proxy代表什么?它代码的是JDK动态生成的代理类的一个实例。
再思考一个问题,动态生成的代理类会干活么?不会,只有我们自己的IWorker实现类Worker才知道work方法要做什么,所以我们必须要有一个Worker实例才能真正完成工作。因此有了下面的版本二。
版本二
publicclass DynamicProxyTest { publicstaticinterface IWorker { public String work(); }
publicstaticclass Worker implements IWorker{ public String work() { System.out.println("working..."); return"hello, work"; } }
publicstaticclass Invocator implements InvocationHandler { private IWorker worker; public Invocator(IWorker worker) { this.worker = worker; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before work..."); String s = (String)method.invoke(worker); System.out.println("after work..."); returns; } } publicstaticvoid main(String[] args) { IWorker worker = new Worker(); InvocationHandler invocator = new Invocator(worker); IWorker proxyWorker = (IWorker)Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(), new Class[]{IWorker.class}, invocator); System.out.println(proxyWorker.work()); } } |
我们让InvocationHandler持有一个Worker实例,有实例、又有实例的方法,通过反射就可以完成具体的工作了。并且它可以在做具体工作前、后做任意的处理。
版本三
从上面版本二可以看出,要获取一个接口的代理类,我们要提供两个参数:接口类和实现类的一个实例。
如果接口类和实现类的包名再加一些约束条件,即通过接口类可以知道实现类的约束条件,那样我们就可以传入一个接口类,就可以返回一个代理类了。比如最简单的约束条件:接口类和实现类在同一包名下,接口类以IXXX命名,实现类以XXXImpl命名。
publicclass DynamicProxyTest { publicstaticinterface IWorker { public String work(); }
publicstaticclass IWorkerImpl implements IWorker{ public String work() { System.out.println("working..."); return"hello, work"; } }
publicstaticclass Invocator implements InvocationHandler { private Object o; private Invocator(Object o) { this.o = o; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before work..."); String s = (String)method.invoke(o); System.out.println("after work..."); returns; } }
publicstatic Object getProxy(Class<?> iface) throws Exception { String ifaceName = iface.getName(); String[] ifaceSplitted = ifaceName.split("[.]");
String packageName = ""; intlastSplit = ifaceName.lastIndexOf("."); if (lastSplit != -1) { packageName = ifaceName.substring(0, lastSplit); }
String className = ifaceSplitted[ifaceSplitted.length - 1] + "Impl"; if (!packageName.equals("")) { className = packageName + "." + className; }
//实例化实现类 Object o = Class.forName(className).newInstance(); InvocationHandler invocator = new Invocator(o); return Proxy.newProxyInstance(DynamicProxyTest.class.getClassLoader(), new Class[]{iface}, invocator); }
publicstaticvoid main(String[] args) throws Exception { IWorker worker = (IWorker)getProxy(IWorker.class); worker.work(); }
/** * 模仿JDK动态生成的代理类 * Java为IWorker生成的代理类,应该类似我们这里实现的该类。 * 只不过JDK使用字节码技术动态生成该类,因此灵活性更高。 * @author Grucee */ publicstaticclass ImitationProxy implements IWorker{ private InvocationHandler handler;
public ImitationProxy(InvocationHandler handler) { this.handler = handler; }
@Override public String work() { /** * Object proxy:这个好办,传入this * Method method:代理类怎么知道自己是有哪些方法要实现的,肯定是通过 * Proxy.newProxyInstance第二个参数(接口类数组),然后通过 * Class:getDeclaredMethods()获取到有哪些方法要实现,所以它在生成代理类的work方法 * 的时候,肯定是知道Method是哪个了。 * Object[] args:方法的参数 */ //return handler.invoke(this, method, args); return""; }
} } |