1、invoke 用法在写代码的时候,发现Method可以调用子类的对象,但子类即使是改写了的Method,方法名一样,去调用父类的对象也会报错,虽然这是很符合多态的现象,也符合java的动态绑定规范,但还是想弄懂java是如何实现的,就学习了下Method的源代码。 Method的invoke方法1.先检查 AccessibleObject的override属性是否为true。AccessibleObject是Method,Field,Constructor的父类,override属性默认为false,可调用setAccessible方法改变,如果设置为true,则表示可以忽略访问权限的限制,直
2、接调用。2.如果不是ture,则要进行访问权限检测。用Reflection的quickCheckMemberAccess方法先检查是不是public的,如果不是再用Reflection.getCallerClass(1)方法获得到调用这个方法的Class,然后做是否有权限访问的校验,校验之后缓存一次,以便下次如果还是这个类来调用就不用去做校验了,直接用上次的结果,(很奇怪用这种方式缓存,因为这种方式如果下次换个类来调用的话,就不用会缓存了,而再验证一遍,把这次的结果做为缓存,但上一次的缓存结果就被冲掉了。这是一个很简单的缓冲机制,只适用于一个类的重复调用)。3.调用MethodAccessor
3、的invoke方法。每个Method对象包含一个root对象,root对象里持有一个MethodAccessor对象。我们获得的Method独享相当于一个root对象的镜像,所有这类Method共享root里的MethodAccessor对象,(这个对象由ReflectionFactory方法生成,ReflectionFactory对象在Method类中是static final的由native方法实例化)。ReflectionFactory生成MethodAccessor:如果noInflation的属性为true则直接返回MethodAccessorGenerator创建的一个Method
4、Accessor。否则返回DelegatingMethodAccessorImpl,并将他与一个NativeMethodAccessorImpl互相引用。但DelegatingMethodAccessorImpl执行invoke方法的时候又委托给NativeMethodAccessorImpl了。再一步深入4.NativeMethodAccessorImpl的invkoe方法:调用natiave方法invoke0执行方法调用.注意这里有一个计数器numInvocations,每调用一次方法+1,当比 ReflectionFactory.inflationThreshold(15)大的时候,用M
5、ethodAccessorGenerator创建一个MethodAccessor,并把之前的DelegatingMethodAccessorImpl引用替换为现在新创建的。下一次DelegatingMethodAccessorImpl就不会再交给NativeMethodAccessorImpl执行了,而是交给新生成的java字节码的MethodAccessor。MethodAccessorGenerator使用了asm字节码动态加载技术,暂不深入研究。总结 一个方法可以生成多个Method对象,但只有一个root对象,主要用于持有一个MethodAccessor对象,这个对象也可以认为一个方法
6、只有一个,相当于是static的。因为Method的invoke是交给MethodAccessor执行的,所以我所想要知道的答案在MethodAccessor的invoke中,深入MethodAccessor:-MethodAccessor-假如有这么一个类A: public class A public void foo(String name) System.out.println(Hello, + name);可以编写另外一个类来反射调用A上的方法: import java.lang.reflect.Method;public class TestClassLoad public sta
7、tic void main(String args) throws Exception Class clz = Class.forName(A);Object o = clz.newInstance();Method m = clz.getMethod(foo, String.class);for (int i = 0; i 16; i+) m.invoke(o, Integer.toString(i);注意到TestClassLoad类上不会有对类A的符号依赖也就是说在加载并初始化TestClassLoad类时不需要关心类A的存在与否,而是等到main()方法执行到调用Class.forNa
8、me()时才试图对类A做动态加载;这里用的是一个参数版的forName(),也就是使用当前方法所在类的ClassLoader来加载,并且初始化新加载的类。好吧这个细节跟主题没啥关系。 回到主题。这次我的测试环境是Sun的JDK 1.6.0 update 13 build 03。编译上述代码,并在执行TestClassLoad时加入-XX:+TraceClassLoading参数(或者-verbose:class或者直接-verbose都行),如下:控制台命令java -XX:+TraTestClassLoad ceClassLoading 可以看到输出了一大堆log,把其中相关的部分截取出来如
9、下:Loaded TestClassLoad from file:/D:/temp_code/test_java_classload/Loaded A from file:/D:/temp_code/test_java_classload/Loaded sun.reflect.NativeMethodAccessorImpl from shared objects fileLoaded sun.reflect.DelegatingMethodAccessorImpl from shared objects fileHello, 0Hello, 1Hello, 2Hello, 3Hello, 4
10、Hello, 5Hello, 6Hello, 7Hello, 8Hello, 9Hello, 10Hello, 11Hello, 12Hello, 13Hello, 14Loaded sun.reflect.ClassFileConstants from shared objects fileLoaded sun.reflect.AccessorGenerator from shared objects fileLoaded sun.reflect.MethodAccessorGenerator from shared objects fileLoaded sun.reflect.ByteVe
11、ctorFactory from shared objects fileLoaded sun.reflect.ByteVector from shared objects fileLoaded sun.reflect.ByteVectorImpl from shared objects fileLoaded sun.reflect.ClassFileAssembler from shared objects fileLoaded sun.reflect.UTF8 from shared objects fileLoaded java.lang.Void from shared objects
12、fileLoaded sun.reflect.Label from shared objects fileLoaded sun.reflect.Label$PatchInfo from shared objects fileLoaded java.util.AbstractList$Itr from shared objects fileLoaded sun.reflect.MethodAccessorGenerator$1 from shared objects fileLoaded sun.reflect.ClassDefiner from shared objects fileLoade
13、d sun.reflect.ClassDefiner$1 from shared objects fileLoaded sun.reflect.GeneratedMethodAccessor1 from _JVM_DefineClass_Hello, 15可以看到前15次反射调用A.foo()方法并没有什么稀奇的地方,但在第16次反射调用时似乎有什么东西被触发了,导致JVM新加载了一堆类,其中就包括Loaded sun.reflect.GeneratedMethodAccessor1 from _JVM_DefineClass_这么一行。这是哪里来的呢? 先来看看JDK里Method.invo
14、ke()是怎么实现的。 java.lang.reflect.Method: public final class Method extends AccessibleObject implements GenericDeclaration, Member / . private volatile MethodAccessor methodAccessor; / For sharing of MethodAccessors. This branching structure is / currently only two levels deep (i.e., one root Method and
15、 / potentially many Method objects pointing to it.) private Method root; / . public Object invoke(Object obj, Object. args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException if (!override) if (!Reflection.quickCheckMemberAccess(clazz, modifiers) Class caller = Reflec
16、tion.getCallerClass(1); Class targetClass = (obj = null | !Modifier.isProtected(modifiers) ? clazz : obj.getClass(); boolean cached; synchronized (this) cached = (securityCheckCache = caller) & (securityCheckTargetClassCache = targetClass); if (!cached) Reflection.ensureMemberAccess(caller, clazz, o
17、bj, modifiers); synchronized (this) securityCheckCache = caller; securityCheckTargetClassCache = targetClass; if (methodAccessor = null) acquireMethodAccessor(); return methodAccessor.invoke(obj, args); / NOTE that there is no synchronization used here. It is correct / (though not efficient) to gene
18、rate more than one MethodAccessor / for a given Method. However, avoiding synchronization will / probably make the implementation more scalable. private void acquireMethodAccessor() / First check to see if one has been created yet, and take it / if so MethodAccessor tmp = null; if (root != null) tmp
19、 = root.getMethodAccessor(); if (tmp != null) methodAccessor = tmp; return; / Otherwise fabricate one and propagate it up to the root tmp = reflectionFactory.newMethodAccessor(this); setMethodAccessor(tmp); / .可以看到Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。 每个实际的Java方法只有一个对应
20、的Method对象作为root,。这个root是不会暴露给用户的,而是每次在通过反射获取Method对象时新创建Method对象把root包装起来再给用户。在第一次调用一个实际Java方法对应得Method对象的invoke()方法之前,实现调用逻辑的MethodAccessor对象还没创建;等第一次调用时才新创建MethodAccessor并更新给root,然后调用MethodAccessor.invoke()真正完成反射调用。 那么MethodAccessor是啥呢? sun.reflect.MethodAccessor:public interface MethodAccessor /*
21、 Matches specification in link java.lang.reflect.Method */ public Object invoke(Object obj, Object args) throws IllegalArgumentException, InvocationTargetException;可以看到它只是一个单方法接口,其invoke()方法与Method.invoke()的对应。 创建MethodAccessor实例的是ReflectionFactory。sun.reflect.ReflectionFactory:public class Reflecti
22、onFactory private static boolean initted = false; / . / / Inflation mechanism. Loading bytecodes to implement / Method.invoke() and Constructor.newInstance() currently costs / 3-4x more than an invocation via native code for the first / invocation (though subsequent invocations have been benchmarked
23、 / to be over 20x faster). Unfortunately this cost increases / startup time for certain applications that use reflection / intensively (but only once per class) to bootstrap themselves. / To avoid this penalty we reuse the existing JVM entry points / for the first few invocations of Methods and Cons
24、tructors and / then switch to the bytecode-based implementations. / / Package-private to be accessible to NativeMethodAccessorImpl / and NativeConstructorAccessorImpl private static boolean noInflation = false; private static int inflationThreshold = 15; / . /* We have to defer full initialization o
25、f this class until after the static initializer is run since java.lang.reflect.Methods static initializer (more properly, that for java.lang.reflect.AccessibleObject) causes this classs to be run, before the system properties are set up. */ private static void checkInitted() if (initted) return; Acc
26、essController.doPrivileged(new PrivilegedAction() public Object run() / Tests to ensure the system properties table is fully / initialized. This is needed because reflection code is / called very early in the initialization process (before / command-line arguments have been parsed and therefore / th
27、ese user-settable properties installed.) We assume that / if System.out is non-null then the System class has been / fully initialized and that the bulk of the startup code / has been run. if (System.out = null) / java.lang.System not yet fully initialized return null; String val = System.getPropert
28、y(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 (NumberFormatException e) throw (RuntimeException) new RuntimeException(Unable to parse
29、property sun.reflect.inflationThreshold). initCause(e); initted = true; return null; ); / . public MethodAccessor newMethodAccessor(Method method) checkInitted(); if (noInflation) return new MethodAccessorGenerator(). generateMethod(method.getDeclaringClass(), method.getName(), method.getParameterTy
30、pes(), method.getReturnType(), method.getExceptionTypes(), method.getModifiers(); else NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method); DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc); acc.setParent(res); return res; 这里就可以看到有趣的地方了。如注释所述,实际的MethodAccessor实现有两个版本,一个是Java实现的,另一个是native code实现的。Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1