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简单登录实例
如有不足之处,欢迎留言指出。