前言:java反射2,本章主要学习总结invoke方法
简单实例 1 2 3 4 5 public class RealSubject { public void doSomething () { System.out.println("execute RealSubject method" ); } }
main.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class main { public static void main (String[] args) { try { 获取Class对象 Class<?> realSubjectClass = RealSubject.class ; Object obj = realSubjectClass.newInstance(); Method method = realSubjectClass.getMethod("doSomething" ); method.invoke(obj); } catch (Exception e) { e.printStackTrace(); } } }
结果
1 execute RealSubject method
Method.invoke 功能:调用对象的方法
Method类图
源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @CallerSensitive public Object invoke (Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { if (!override) { if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers); } } MethodAccessor ma = methodAccessor; if (ma == null ) { ma = acquireMethodAccessor(); } return ma.invoke(obj, args); }
权限检查
检查AccessibleObject的override属性的值。
AccessibleObject 类是Field类、Method类和Constructor类基类。
若override的值为true,表示忽略权限规则,调用方法时无需检查权限(也就是说可以调用任意的private方法,这违反了封装)
当override的值默认是false,表示需要权限调用规则,调用方法时需要检查权限 如果override属性为默认值false,则进行进一步的权限检查
1 2 3 4 if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) { }
Method类 部分属性
1 2 3 4 5 public final class Method extends Executable { private Class<?> clazz; private int modifiers; …… }
使用Reflection.quickCheckMemberAccess(clazz, modifiers)方法检查方法是否为public,其中clazz(clazz用于替代保留字class), modifiers均是Method变量;
Method.invoke()方法部分
1 2 Class<?> caller = Reflection.getCallerClass(); checkAccess(caller, clazz, obj, modifiers);
如果不是public方法,那么用Reflection.getCallerClass()方法可以得到调用者的类,这是一个native方法(源码在jvm.cpp中):
获取了这个Class对象caller后用checkAccess方法做一次权限校验,源码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void checkAccess (Class<?> caller, Class<?> clazz, Object obj, int modifiers) throws IllegalAccessException { if (caller == clazz) { return ; } Object cache = securityCheckCache; Class<?> targetClass = clazz; if (obj != null && Modifier.isProtected(modifiers) && ((targetClass = obj.getClass()) != clazz)) { if (cache instanceof Class[]) { Class<?>[] cache2 = (Class<?>[]) cache; if (cache2[1 ] == targetClass && cache2[0 ] == caller) { return ; } } } else if (cache == caller) { return ; } slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass); }
首先执行一次quick check,一旦调用方法的caller == clazz则权限检查通过。 若未通过,则创建一个安全校验缓存,进行是否为protected访问修饰符等判断
AccessibleObject.slowCheckMemberAccess 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void slowCheckMemberAccess (Class<?> caller, Class<?> clazz, Object obj, int modifiers, Class<?> targetClass) throws IllegalAccessException { Reflection.ensureMemberAccess(caller, clazz, obj, modifiers); Object cache = ((targetClass == clazz) ? caller : new Class<?>[] { caller, targetClass }); securityCheckCache = cache; }
Reflection.ensureMemberAccess 静态方法
1 2 3 4 5 6 7 8 9 public static void ensureMemberAccess (Class<?> var0, Class<?> var1, Object var2, int var3) throws IllegalAccessException { if (var0 != null && var1 != null ) { if (!verifyMemberAccess(var0, var1, var2, var3)) { throw new IllegalAccessException("Class " + var0.getName() + " can not access a member of class " + var1.getName() + " with modifiers \"" + Modifier.toString(var3) + "\"" ); } } else { throw new InternalError(); } }
MethodAccessor Method.invoke()方法部分
1 2 3 4 MethodAccessor ma = methodAccessor; if (ma == null ) { ma = acquireMethodAccessor(); }
Method 类 methodAccessor属性
1 2 3 4 public final class Method extends Executable { private volatile MethodAccessor methodAccessor; private Method root; }
Method.invoke()实际上并不是自己实现的反射调用逻辑,而是委托给sun.reflect.MethodAccessor来处理。 首先要了解Method对象的基本构成,每个Java方法有且只有一个Method对象作为root,它相当于根对象,对用户不可见,如同Class对象,JVM也只加载并储存一个。当我们创建Method对象时,我们代码中获得的Method对象都相当于它的副本(或引用)。root对象持有一个MethodAccessor对象,所以所有获取到的Method对象都共享这一个MethodAccessor对象,因此必须保证它在内存中的可见性。root对象其声明及注释为:
1 2 3 public interface MethodAccessor { Object invoke (Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException ; }
MethodAccessor接口的实现类
java.lang.reflect.Method 查看acquireMethodAccessor方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private MethodAccessor acquireMethodAccessor () { MethodAccessor tmp = null ; if (root != null ) tmp = root.getMethodAccessor(); if (tmp != null ) { methodAccessor = tmp; } else { tmp = reflectionFactory.newMethodAccessor(this ); setMethodAccessor(tmp); } return tmp; }
第一次调用ReflectionFactory对象的newMethodAccessor方法生成一个MethodAccessor对象,并更新给root。之后调用通过root获取,然后调用MethodAccessor.invoke()完成反射调用,下面查看reflectionFactory和其调用的方法newMethodAccessor
java.lang.reflect.AccessibleObject 1 2 3 static final ReflectionFactory reflectionFactory = AccessController.doPrivileged( new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
reflectionFactory是AccessibleObject类的静态常量
sun.reflect.ReflectionFactory 部分源码 ReflectionFactory类是反射工厂类,负责复制Filed类、Method类、Constructor类(在JVM加载.class时创建),返回的对象用于判定三者的修饰符权限的,也就是实现了当方法是私有方法时,外部不能访问它的功能的,原理是当外部要访问这个私有方法时,会创建一个方法的MethodAccessor,并在里面进行相关的判断,判断如果是访问的权限那么允许访问者访问,否则拒绝抛出异常,其它两者也是一样的,而且不仅仅会判断修饰符的权限也会判断其属性的类型,方法的类型等。
1 2 3 4 5 6 7 8 9 10 11 12 13 public FieldAccessor newFieldAccessor (Field var1, boolean var2) {……}public ConstructorAccessor newConstructorAccessor (Constructor<?> var1) {……}public MethodAccessor newMethodAccessor (Method var1) { checkInitted(); if (noInflation && !ReflectUtil.isVMAnonymousClass(var1.getDeclaringClass())) { return (new MethodAccessorGenerator()).generateMethod(var1.getDeclaringClass(), var1.getName(), var1.getParameterTypes(), var1.getReturnType(), var1.getExceptionTypes(), var1.getModifiers()); } else { NativeMethodAccessorImpl var2 = new NativeMethodAccessorImpl(var1); DelegatingMethodAccessorImpl var3 = new DelegatingMethodAccessorImpl(var2); var2.setParent(var3); return var3; } }
checkInitted()方法源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 private static void checkInitted () { if (!initted) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run () { if (System.out == null ) { return null ; } else { String var1 = System.getProperty("sun.reflect.noInflation" ); if (var1 != null && var1.equals("true" )) { ReflectionFactory.noInflation = true ; } var1 = System.getProperty("sun.reflect.inflationThreshold" ); if (var1 != null ) { try { ReflectionFactory.inflationThreshold = Integer.parseInt(var1); } catch (NumberFormatException var3) { throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold" , var3); } } ReflectionFactory.initted = true ; return null ; } } }); } }
checkInitted()方法检查是否初始化,没有则从配置项中读取配置并设置noInflation、inflationThreshold的值
noInflation、inflationThreshold的官方注释 PS:我用idea查看源码没有,下面注释来自jdk8文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private static boolean noInflation = false ;private static int inflationThreshold = 15 ;
渣翻
Inflation机制(膨胀机制)。初次加载字节码实现反射,使用method.invoke()和constructor.newInstance()时 比通过原生代码调用加载快3-4倍(尽管经过基准测试发现后续调用快20倍以上。但不幸的是, 某些频繁使用反射的程序的需要花费更多的启动时间 为了避免加载的惩罚,我们在第一次加载重用现有的JVM入口点对于方法和构造函数的前几个调用,之后切换到基于字节码的实现。
DelegatingMethodAccessorImpl 源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class DelegatingMethodAccessorImpl extends MethodAccessorImpl { private MethodAccessorImpl delegate; DelegatingMethodAccessorImpl(MethodAccessorImpl var1) { this .setDelegate(var1); } public Object invoke (Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException { return this .delegate.invoke(var1, var2); } void setDelegate (MethodAccessorImpl var1) { this .delegate = var1; } }
在ReflectionFactory类的newMethodAccessor方法里,生成 NativeMethodAccessorImpl对象,并由DelegatingMethodAccessorImpl对象代理((这是运用了代理模式),返回DelegatingMethodAccessorImpl对象。所以invoke方法最终调用时是DelegatingMethodAccessorImpl.invoke,DelegatingMethodAccessorImpl.invoke调用NativeMethodAccessorImpl.invoke()
NativeMethodAccessorImpl源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class NativeMethodAccessorImpl extends MethodAccessorImpl { private final Method method; private DelegatingMethodAccessorImpl parent; private int numInvocations; NativeMethodAccessorImpl(Method var1) { this .method = var1; } public Object invoke (Object var1, Object[] var2) throws IllegalArgumentException, InvocationTargetException { if (++this .numInvocations > ReflectionFactory.inflationThreshold() && !ReflectUtil.isVMAnonymousClass(this .method.getDeclaringClass())) { MethodAccessorImpl var3 = (MethodAccessorImpl)(new MethodAccessorGenerator()).generateMethod(this .method.getDeclaringClass(), this .method.getName(), this .method.getParameterTypes(), this .method.getReturnType(), this .method.getExceptionTypes(), this .method.getModifiers()); this .parent.setDelegate(var3); } return invoke0(this .method, var1, var2); } void setParent (DelegatingMethodAccessorImpl var1) { this .parent = var1; } private static native Object invoke0 (Method var0, Object var1, Object[] var2) ; }
NativeMethodAccessorImpl.invoke方法中,每次调用计数增加,并判断调用次数是否超过阀值(numInvocations)。一旦超过,则调用MethodAccessorGenerator.generateMethod()来生成Java版的MethodAccessor的实现类,并且改变DelegatingMethodAccessorImpl所引用的MethodAccessor为Java版,并将原来DelegatingMethodAccessorImpl对象中的delegate属性指向最新的MethodAccessor对象。注意到关键的invoke0()方法是个native方法,所以其代码就是由底层的C实现(代码在HotSpot VM中)
根据上面的注释,就清楚了实际的MethodAccessor实现有两个版本,一个是Java版本即使用MethodAccessorGenerator类实现,一个是native版本即使用native的invoke0方法实现,两者各有特点。初次启动时Method.invoke()和Constructor.newInstance()方法采用native方法要比Java方法快3-4倍,而启动后native方法又要消耗额外的性能而慢于Java方法。也就是说,Java实现的版本在初始化时需要较多时间,但长久来说性能较好;native版本正好相反,启动时相对较快,但运行时间长了之后速度就比不过Java版了。这是HotSpot的优化方式带来的性能特性,同时也是许多虚拟机的共同点:跨越native边界会对优化有阻碍作用,它就像个黑箱一样让虚拟机难以分析也将其内联,于是运行时间长了之后反而是托管版本的代码更快些。
为了尽可能地减少性能损耗,HotSpot JDK采用“inflation”的技巧:让Java方法在被反射调用时,开头若干次使用native版,等反射调用次数超过阈值时则生成一个专用的MethodAccessor实现类,生成其中的invoke()方法的字节码,以后对该Java方法的反射调用就会使用Java版本。 这项优化是从JDK 1.4开始的。
invoke0方法 invoke0方法是一个native方法,它在HotSpot JVM里调用JVM_InvokeMethod函数
1 2 3 4 5 JNIEXPORT jobject JNICALL Java_sun_reflect_NativeMethodAccessorImpl_invoke0 (JNIEnv *env, jclass unused, jobject m, jobject obj, jobjectArray args){ return JVM_InvokeMethod(env, m, obj, args); }
openjdk/hotspot/src/share/vm/prims/jvm.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 JVM_ENTRY(jobject, JVM_InvokeMethod(JNIEnv *env, jobject method, jobject obj, jobjectArray args0)) JVMWrapper("JVM_InvokeMethod" ); Handle method_handle; if (thread->stack_available((address) &method_handle) >= JVMInvokeMethodSlack) { method_handle = Handle(THREAD, JNIHandles::resolve(method)); Handle receiver (THREAD, JNIHandles::resolve(obj) ) ; objArrayHandle args (THREAD, objArrayOop(JNIHandles::resolve(args0) )) ; oop result = Reflection::invoke_method(method_handle(), receiver, args, CHECK_NULL); jobject res = JNIHandles::make_local(env, result); if (JvmtiExport::should_post_vm_object_alloc()) { oop ret_type = java_lang_reflect_Method::return_type(method_handle()); assert (ret_type != NULL, "sanity check: ret_type oop must not be NULL!" ); if (java_lang_Class::is_primitive(ret_type)) { JvmtiExport::post_vm_object_alloc(JavaThread::current(), result); } } return res; } else { THROW_0(vmSymbols::java_lang_StackOverflowError()); } JVM_END
其关键部分为Reflection::invoke_method:openjdk/hotspot/src/share/vm/runtime/reflection.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 oop Reflection::invoke_method(oop method_mirror, Handle receiver, objArrayHandle args, TRAPS) { oop mirror = java_lang_reflect_Method::clazz(method_mirror); int slot = java_lang_reflect_Method::slot(method_mirror); bool override = java_lang_reflect_Method::override(method_mirror) != 0 ; objArrayHandle ptypes (THREAD, objArrayOop(java_lang_reflect_Method::parameter_types(method_mirror) )) ; oop return_type_mirror = java_lang_reflect_Method::return_type(method_mirror); BasicType rtype; if (java_lang_Class::is_primitive(return_type_mirror)) { rtype = basic_type_mirror_to_basic_type(return_type_mirror, CHECK_NULL); } else { rtype = T_OBJECT; } instanceKlassHandle klass (THREAD, java_lang_Class::as_Klass(mirror) ) ; Method* m = klass->method_with_idnum(slot); if (m == NULL) { THROW_MSG_0(vmSymbols::java_lang_InternalError(), "invoke" ); } methodHandle method (THREAD, m) ; return invoke(klass, method, receiver, override, ptypes, rtype, args, true , THREAD); }
@CallerSensitive 注解 用于精确识别caller-sensitive方法并且保证这些方法的调用者可靠地被发现的一种机制,代替现存的手动维护的caller-sensitive方法表,提高JDK method-handler实现的安全性。
这个注解是为了堵住漏洞用的。曾经有黑客通过构造双重反射来提升权限, 原理是当时反射只检查固定深度的调用者的类,看它有没有特权, 例如固定看两层的调用者(getCallerClass(2))。如果我的类本来没足够 权限群访问某些信息,那我就可以通过双重反射去达到目的:反射相关 的类是有很高权限的,而在 我->反射1->反射2 这样的调用链上,反射2 检查权限时看到的是反射1的类,这就被欺骗了,导致安全漏洞。 使用CallerSensitive后,getCallerClass不再用固定深度去寻找 actual caller(“我”),而是把所有跟反射相关的接口方法都标注上 CallerSensitive,搜索时凡看到该注解都直接跳过,这样就有效解决了 前面举例的问题
Method.invoke()大佬总结图(个人搬运):