InvocationHandler中invoke()方法的调用问题分析
Java中动态代理的实现,关键就是这两个东西:Proxy、InvocationHandler,下面从InvocationHandler接口中的invoke方法入手,简单说明一下Java如何实现动态代理的。
首先,invoke方法的完整形式如下:
publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable { method.invoke(obj,args); returnnull; }
首先猜测一下,method是调用的方法,即需要执行的方法;args是方法的参数;proxy,这个参数是什么?以上invoke()方法的实现即是比较标准的形式,我们看到,这里并没有用到proxy参数。查看JDK文档中Proxy的说明,如下:
Amethodinvocationonaproxyinstancethroughoneofitsproxyinterfaceswillbedispatchedtotheinvokemethodoftheinstance'sinvocationhandler,passingtheproxyinstance,ajava.lang.reflect.Methodobjectidentifyingthemethodthatwasinvoked,andanarrayoftypeObjectcontainingthearguments.
由此可以知道以上的猜测是正确的,同时也知道,proxy参数传递的即是代理类的实例。
为了方便说明,这里写一个简单的例子来实现动态代理。
//抽象角色(动态代理只能代理接口) publicinterfaceSubject{ publicvoidrequest(); }
//真实角色:实现了Subject的request()方法 publicclassRealSubjectimplementsSubject{ publicvoidrequest(){ System.out.println("Fromrealsubject."); } }
//实现了InvocationHandler publicclassDynamicSubjectimplementsInvocationHandler { privateObjectobj;//这是动态代理的好处,被封装的对象是Object类型,接受任意类型的对象 publicDynamicSubject() { } publicDynamicSubject(Objectobj) { this.obj=obj; } //这个方法不是我们显示的去调用 publicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable { System.out.println("beforecalling"+method); method.invoke(obj,args); System.out.println("aftercalling"+method); returnnull; } }
//客户端:生成代理实例,并调用了request()方法 publicclassClient{ publicstaticvoidmain(String[]args)throwsThrowable{ //TODOAuto-generatedmethodstub Subjectrs=newRealSubject();//这里指定被代理类 InvocationHandlerds=newDynamicSubject(rs); Class>cls=rs.getClass(); //以下是一次性生成代理 Subjectsubject=(Subject)Proxy.newProxyInstance( cls.getClassLoader(),cls.getInterfaces(),ds); //这里可以通过运行结果证明subject是Proxy的一个实例,这个实例实现了Subject接口 System.out.println(subjectinstanceofProxy); //这里可以看出subject的Class类是$Proxy0,这个$Proxy0类继承了Proxy,实现了Subject接口 System.out.println("subject的Class类是:"+subject.getClass().toString()); System.out.print("subject中的属性有:"); Field[]field=subject.getClass().getDeclaredFields(); for(Fieldf:field){ System.out.print(f.getName()+","); } System.out.print("\n"+"subject中的方法有:"); Method[]method=subject.getClass().getDeclaredMethods(); for(Methodm:method){ System.out.print(m.getName()+","); } System.out.println("\n"+"subject的父类是:"+subject.getClass().getSuperclass()); System.out.print("\n"+"subject实现的接口是:"); Class>[]interfaces=subject.getClass().getInterfaces(); for(Class>i:interfaces){ System.out.print(i.getName()+","); } System.out.println("\n\n"+"运行结果为:"); subject.request(); } }
运行结果如下:此处省略了包名,***代替
true
subject的Class类是:class$Proxy0
subject中的属性有:m1,m3,m0,m2,
subject中的方法有:request,hashCode,equals,toString,
subject的父类是:classjava.lang.reflect.Proxy
subject实现的接口是:cn.edu.ustc.dynamicproxy.Subject,
运行结果为:
beforecallingpublicabstractvoid***.Subject.request()
Fromrealsubject.
aftercallingpublicabstractvoid***.Subject.request()
PS:这个结果的信息非常重要,至少对我来说。因为我在动态代理犯晕的根源就在于将上面的subject.request()理解错了,至少是被表面所迷惑,没有发现这个subject和Proxy之间的联系,一度纠结于最后调用的这个request()是怎么和invoke()联系上的,而invoke又是怎么知道request存在的。其实上面的true和class$Proxy0就能解决很多的疑问,再加上下面将要说的$Proxy0的源码,完全可以解决动态代理的疑惑了。
从以上代码和结果可以看出,我们并没有显示的调用invoke()方法,但是这个方法确实执行了。下面就整个的过程进行分析一下:
从Client中的代码看,可以从newProxyInstance这个方法作为突破口,我们先来看一下Proxy类中newProxyInstance方法的源代码:
publicstaticObjectnewProxyInstance(ClassLoaderloader, Class>[]interfaces, InvocationHandlerh) throwsIllegalArgumentException { if(h==null){ thrownewNullPointerException(); } /* *Lookuporgeneratethedesignatedproxyclass. */ Classcl=getProxyClass(loader,interfaces); /* *Invokeitsconstructorwiththedesignatedinvocationhandler. */ try{ /* *Proxy源码开始有这样的定义: *privatefinalstaticClass[]constructorParams={InvocationHandler.class}; *cons即是形参为InvocationHandler类型的构造方法 */ Constructorcons=cl.getConstructor(constructorParams); return(Object)cons.newInstance(newObject[]{h}); }catch(NoSuchMethodExceptione){ thrownewInternalError(e.toString()); }catch(IllegalAccessExceptione){ thrownewInternalError(e.toString()); }catch(InstantiationExceptione){ thrownewInternalError(e.toString()); }catch(InvocationTargetExceptione){ thrownewInternalError(e.toString()); } }
Proxy.newProxyInstance(ClassLoaderloader,Class>[]interfaces,InvocationHandlerh)做了以下几件事.
(1)根据参数loader和interfaces调用方法getProxyClass(loader,interfaces)创建代理类$Proxy0.$Proxy0类实现了interfaces的接口,并继承了Proxy类.
(2)实例化$Proxy0并在构造方法中把DynamicSubject传过去,接着$Proxy0调用父类Proxy的构造器,为h赋值,如下:
classProxy{ InvocationHandlerh=null; protectedProxy(InvocationHandlerh){ this.h=h; } ... }
来看一下这个继承了Proxy的$Proxy0的源代码:
publicfinalclass$Proxy0extendsProxyimplementsSubject{ privatestaticMethodm1; privatestaticMethodm0; privatestaticMethodm3; privatestaticMethodm2; static{ try{ m1=Class.forName("java.lang.Object").getMethod("equals", newClass[]{Class.forName("java.lang.Object")}); m0=Class.forName("java.lang.Object").getMethod("hashCode", newClass[0]); m3=Class.forName("***.RealSubject").getMethod("request", newClass[0]); m2=Class.forName("java.lang.Object").getMethod("toString", newClass[0]); }catch(NoSuchMethodExceptionnosuchmethodexception){ thrownewNoSuchMethodError(nosuchmethodexception.getMessage()); }catch(ClassNotFoundExceptionclassnotfoundexception){ thrownewNoClassDefFoundError(classnotfoundexception.getMessage()); } }//static public$Proxy0(InvocationHandlerinvocationhandler){ super(invocationhandler); } @Override publicfinalbooleanequals(Objectobj){ try{ return((Boolean)super.h.invoke(this,m1,newObject[]{obj})).booleanValue(); }catch(Throwablethrowable){ thrownewUndeclaredThrowableException(throwable); } } @Override publicfinalinthashCode(){ try{ return((Integer)super.h.invoke(this,m0,null)).intValue(); }catch(Throwablethrowable){ thrownewUndeclaredThrowableException(throwable); } } publicfinalvoidrequest(){ try{ super.h.invoke(this,m3,null); return; }catch(Errore){ }catch(Throwablethrowable){ thrownewUndeclaredThrowableException(throwable); } } @Override publicfinalStringtoString(){ try{ return(String)super.h.invoke(this,m2,null); }catch(Throwablethrowable){ thrownewUndeclaredThrowableException(throwable); } } }
接着把得到的$Proxy0实例强制转换成Subject,并将引用赋给subject。当执行subject.request()方法时,就调用了$Proxy0类中的request()方法,进而调用父类Proxy中的h的invoke()方法.即InvocationHandler.invoke()。
PS:1、需要说明的一点是,Proxy类中getProxyClass方法返回的是Proxy的Class类。之所以说明,是因为我一开始犯了个低级错误,以为返回的是“被代理类的Class类”--!推荐看一下getProxyClass的源码,很长=。=
2、从$Proxy0的源码可以看出,动态代理类不仅代理了显示定义的接口中的方法,而且还代理了java的根类Object中的继承而来的equals()、hashcode()、toString()这三个方法,并且仅此三个方法。
Q:到现在为止,还有一个疑问,invoke方法中的第一个参数是Proxy的实例(准确说,最终用到的是$Proxy0的实例),但是有什么用呢?或者说,程序内是怎样显示出作用的?
A:就本人目前的水平看来,这个proxy参数并没有什么作用,在整个动态代理机制中,并没有用到InvocationHandler中invoke方法的proxy参数。而传入的这个参数实际是代理类的一个实例。我想可能是为了让程序员在invoke方法中使用反射来获取关于代理类的一些信息吧。
总结
以上就是本文关于InvocationHandler中invoke()方法的调用问题分析的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:
Spring静态代理和动态代理代码详解
Spring框架依赖注入方法示例
Java编程实现springMVC简单登录实例
如有不足之处,欢迎留言指出。