invoke 用法.docx

上传人:b****8 文档编号:28616704 上传时间:2023-07-19 格式:DOCX 页数:22 大小:27.63KB
下载 相关 举报
invoke 用法.docx_第1页
第1页 / 共22页
invoke 用法.docx_第2页
第2页 / 共22页
invoke 用法.docx_第3页
第3页 / 共22页
invoke 用法.docx_第4页
第4页 / 共22页
invoke 用法.docx_第5页
第5页 / 共22页
点击查看更多>>
下载资源
资源描述

invoke 用法.docx

《invoke 用法.docx》由会员分享,可在线阅读,更多相关《invoke 用法.docx(22页珍藏版)》请在冰豆网上搜索。

invoke 用法.docx

invoke用法

在写代码的时候,发现Method可以调用子类的对象,但子类即使是改写了的Method,方法名一样,去调用父类的对象也会报错,虽然这是很符合多态的现象,也符合java的动态绑定规范,但还是想弄懂java是如何实现的,就学习了下Method的源代码。

Method的invoke方法

1.先检查AccessibleObject的override属性是否为true。

AccessibleObject是Method,Field,Constructor的父类,override属性默认为false,可调用setAccessible方法改变,如果设置为true,则表示可以忽略访问权限的限制,直接调用。

2.如果不是ture,则要进行访问权限检测。

用Reflection的quickCheckMemberAccess方法先检查是不是public的,如果不是再用Reflection.getCallerClass

(1)方法获

得到调用这个方法的Class,然后做是否有权限访问的校验,校验之后缓存一次,以便下次如果还是这个类来调用就不用去做校验了,直接用上次的结果,(很奇怪用这种方式缓存,因为这种方式如果下次换个类来调用的话,就不用会缓存了,而再验证一遍,把这次的结果做为缓存,但上一次的缓存结果就被冲掉了。

这是一个很简单的缓冲机制,只适用于一个类的重复调用)。

3.调用MethodAccessor的invoke方法。

每个Method对象包含一个root对象,root对象里持有一个MethodAccessor对象。

我们获得的Method独享相当于一个root对象的镜像,所有这类Method共享root里的MethodAccessor对象,(这个对象由ReflectionFactory方法生成,ReflectionFactory对象在Method类中是staticfinal的由native方法实例化)。

ReflectionFactory生成MethodAccessor:

如果noInflation的属性为true则直接返回MethodAccessorGenerator创建的一个MethodAccessor。

否则返回DelegatingMethodAccessorImpl,并将他与一个NativeMethodAccessorImpl互相引用。

但DelegatingMethodAccessorImpl执行invoke方法的时候又委托给NativeMethodAccessorImpl了。

再一步深入

4.NativeMethodAccessorImpl的invkoe方法:

调用natiave方法invoke0执行方法调用.

注意这里有一个计数器numInvocations,每调用一次方法+1,当比ReflectionFactory.inflationThreshold(15)大的时候,用MethodAccessorGenerator创建一个MethodAccessor,并把之前的DelegatingMethodAccessorImpl引用替换为现在新创建的。

下一次DelegatingMethodAccessorImpl就不会再交给NativeMethodAccessorImpl执行了,而是交给新生成的java字节码的MethodAccessor。

MethodAccessorGenerator使用了asm字节码动态加载技术,暂不深入研究。

总结一个方法可以生成多个Method对象,但只有一个root对象,主要用于持有一个MethodAccessor对象,这个对象也可以认为一个方法只有一个,相当于是static的。

因为Method的invoke是交给MethodAccessor执行的,所以我所想要知道的答案在MethodAccessor的invoke中,深入MethodAccessor:

------------------------------------------MethodAccessor---------------------------------

假如有这么一个类A:

publicclassA{

  publicvoidfoo(Stringname){

    System.out.println("Hello,"+name);

  }

}

可以编写另外一个类来反射调用A上的方法:

importjava.lang.reflect.Method;

publicclassTestClassLoad{

  publicstaticvoidmain(String[]args)throwsException{

    Class

>clz=Class.forName("A");

    Objecto=clz.newInstance();

    Methodm=clz.getMethod("foo",String.class);

    for(inti=0;i<16;i++){

      m.invoke(o,Integer.toString(i));

    }

  }

}

注意到TestClassLoad类上不会有对类A的符号依赖——也就是说在加载并初始化TestClassLoad类时不需要关心类A的存在与否,而是等到main()方法执行到调用Class.forName()时才试图对类A做动态加载;这里用的是一个参数版的forName(),也就是使用当前方法所在类的ClassLoader来加载,并且初始化新加载的类。

……好吧这个细节跟主题没啥关系。

回到主题。

这次我的测试环境是Sun的JDK1.6.0update13build03。

编译上述代码,并在执行TestClassLoad时加入-XX:

+TraceClassLoading参数(或者-verbose:

class或者直接-verbose都行),如下:

控制台命令

java-XX:

+TraTestClassLoadceClassLoading

可以看到输出了一大堆log,把其中相关的部分截取出来如下:

[LoadedTestClassLoadfromfile:

/D:

/temp_code/test_java_classload/]

[LoadedAfromfile:

/D:

/temp_code/test_java_classload/]

[Loadedsun.reflect.NativeMethodAccessorImplfromsharedobjectsfile]

[Loadedsun.reflect.DelegatingMethodAccessorImplfromsharedobjectsfile]

Hello,0

Hello,1

Hello,2

Hello,3

Hello,4

Hello,5

Hello,6

Hello,7

Hello,8

Hello,9

Hello,10

Hello,11

Hello,12

Hello,13

Hello,14

[Loadedsun.reflect.ClassFileConstantsfromsharedobjectsfile]

[Loadedsun.reflect.AccessorGeneratorfromsharedobjectsfile]

[Loadedsun.reflect.MethodAccessorGeneratorfromsharedobjectsfile]

[Loadedsun.reflect.ByteVectorFactoryfromsharedobjectsfile]

[Loadedsun.reflect.ByteVectorfromsharedobjectsfile]

[Loadedsun.reflect.ByteVectorImplfromsharedobjectsfile]

[Loadedsun.reflect.ClassFileAssemblerfromsharedobjectsfile]

[Loadedsun.reflect.UTF8fromsharedobjectsfile]

[Loadedjava.lang.Voidfromsharedobjectsfile]

[Loadedsun.reflect.Labelfromsharedobjectsfile]

[Loadedsun.reflect.Label$PatchInfofromsharedobjectsfile]

[Loadedjava.util.AbstractList$Itrfromsharedobjectsfile]

[Loadedsun.reflect.MethodAccessorGenerator$1fromsharedobjectsfile]

[Loadedsun.reflect.ClassDefinerfromsharedobjectsfile]

[Loadedsun.reflect.ClassDefiner$1fromsharedobjectsfile]

[Loadedsun.reflect.GeneratedMethodAccessor1from__JVM_DefineClass__]

Hello,15

可以看到前15次反射调用A.foo()方法并没有什么稀奇的地方,但在第16次反射调用时似乎有什么东西被触发了,导致JVM新加载了一堆类,其中就包括[Loadedsun.reflect.GeneratedMethodAccessor1from__JVM_DefineClass__]这么一行。

这是哪里来的呢?

先来看看JDK里Method.invoke()是怎么实现的。

java.lang.reflect.Method:

publicfinal

classMethodextendsAccessibleObjectimplementsGenericDeclaration,

Member{

//...

privatevolatileMethodAccessormethodAccessor;

//ForsharingofMethodAccessors.Thisbranchingstructureis

//currentlyonlytwolevelsdeep(i.e.,onerootMethodand

//potentiallymanyMethodobjectspointingtoit.)

privateMethodroot;

//...

publicObjectinvoke(Objectobj,Object...args)

throwsIllegalAccessException,IllegalArgumentException,

InvocationTargetException

{

if(!

override){

if(!

Reflection.quickCheckMemberAccess(clazz,modifiers)){

Classcaller=Reflection.getCallerClass

(1);

ClasstargetClass=((obj==null||!

Modifier.isProtected(modifiers))

?

clazz

:

obj.getClass());

booleancached;

synchronized(this){

cached=(securityCheckCache==caller)

&&(securityCheckTargetClassCache==targetClass);

}

if(!

cached){

Reflection.ensureMemberAccess(caller,clazz,obj,modifiers);

synchronized(this){

securityCheckCache=caller;

securityCheckTargetClassCache=targetClass;

}

}

}

}

if(methodAccessor==null)acquireMethodAccessor();

returnmethodAccessor.invoke(obj,args);

}

//NOTEthatthereisnosynchronizationusedhere.Itiscorrect

//(thoughnotefficient)togeneratemorethanoneMethodAccessor

//foragivenMethod.However,avoidingsynchronizationwill

//probablymaketheimplementationmorescalable.

privatevoidacquireMethodAccessor(){

//Firstchecktoseeifonehasbeencreatedyet,andtakeit

//ifso

MethodAccessortmp=null;

if(root!

=null)tmp=root.getMethodAccessor();

if(tmp!

=null){

methodAccessor=tmp;

return;

}

//Otherwisefabricateoneandpropagateituptotheroot

tmp=reflectionFactory.newMethodAccessor(this);

setMethodAccessor(tmp);

}

//...

}

  可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。

每个实际的Java方法只有一个对应的Method对象作为root,。

这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时新创建Method对象把root包装起来再给用户。

在第一次调用一个实际Java方法对应得Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没创建;等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()真正完成反射调用。

那么MethodAccessor是啥呢?

sun.reflect.MethodAccessor:

publicinterfaceMethodAccessor{

/**Matchesspecificationin{@linkjava.lang.reflect.Method}*/

publicObjectinvoke(Objectobj,Object[]args)

throwsIllegalArgumentException,InvocationTargetException;

}

  

  可以看到它只是一个单方法接口,其invoke()方法与Method.invoke()的对应。

创建MethodAccessor实例的是ReflectionFactory。

sun.reflect.ReflectionFactory:

publicclassReflectionFactory{

privatestaticbooleaninitted=false;

//...

//

//"Inflation"mechanism.Loadingbytecodestoimplement

//Method.invoke()andConstructor.newInstance()currentlycosts

//3-4xmorethananinvocationvianativecodeforthefirst

//invocation(thoughsubsequentinvocationshavebeenbenchmarked

//tobeover20xfaster).Unfortunatelythiscostincreases

//startuptimeforcertainapplicationsthatusereflection

//intensively(butonlyonceperclass)tobootstrapthemselves.

//ToavoidthispenaltywereusetheexistingJVMentrypoints

//forthefirstfewinvocationsofMethodsandConstructorsand

//thenswitchtothebytecode-basedimplementations.

//

//Package-privatetobeaccessibletoNativeMethodAccessorImpl

//andNativeConstructorAccessorImpl

privatestaticbooleannoInflation=false;

privatestaticintinflationThreshold=15;

//...

/**Wehavetodeferfullinitializationofthisclassuntilafter

thestaticinitializerisrunsincejava.lang.reflect.Method's

staticinitializer(moreproperly,thatfor

java.lang.reflect.AccessibleObject)causesthisclass'stobe

run,beforethesystempropertiesaresetup.*/

privatestaticvoidcheckInitted(){

if(initted)return;

AccessController.doPrivileged(newPrivilegedAction(){

publicObjectrun(){

//Teststoensurethesystempropertiestableisfully

//initialized.Thisisneededbecausereflectioncodeis

//calledveryearlyintheinitializationprocess(before

//command-lineargumentshavebeenparsedandtherefore

//theseuser-settablepropertiesinstalled.)Weassumethat

//ifSystem.outisnon-nullthentheSystemclasshasbeen

//fullyinitializedandthatthebulkofthestartupcode

//hasbeenrun.

if(System.out==null){

//java.lang.Systemnotyetfullyinitialized

returnnull;

}

Stringval=System.getProperty("sun.reflect.noInflation");

if(val!

=null&&val.equals("true")){

noInflation=true;

}

val=System.getProperty("sun.reflect.inflationThreshold");

if(val!

=null){

try{

inflationThreshold=Integer.parseInt(val);

}catch(NumberFormatExceptione){

throw(RuntimeException)

newRuntimeException("Unabletoparsepropertysun.reflect.inflationThreshold").

initCause(e);

}

}

initted=true;

returnnull;

}

});

}

//...

publicMethodAccessornewMethodAccessor(Methodmethod){

checkInitted();

if(noInflation){

returnnewMethodAccessorGenerator().

generateMethod(method.getDeclaringClass(),

method.getName(),

method.getParameterTypes(),

method.getReturnType(),

method.getExceptionTypes(),

method.getModifiers());

}else{

NativeMethodAccessorImplacc=

newNativeMethodAccessorImpl(method);

DelegatingMethodAccessorImplres=

newDelegatingMethodAccessorImpl(acc);

acc.setParent(res);

returnres;

}

}

}

  这里就可以看到有趣的地方了。

如注释所述,实际的MethodAccessor实现有两个版本,一个是Java实现的,另一个是nativecode实现的。

Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。

这是HotSpot的优化方式带来的

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 职业教育 > 其它

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1