invoke 用法.docx
《invoke 用法.docx》由会员分享,可在线阅读,更多相关《invoke 用法.docx(22页珍藏版)》请在冰豆网上搜索。
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的优化方式带来的