乐趣区

关于反射:深入理解Java中的反射机制和使用原理详细解析invoke方法的执行和使用

@toc

反射的概念

  • 反射: Refelection, 反射是 Java 的特色之一, 容许 运行 中的 Java 程序获取本身信息, 并能够操作类或者对象的外部属性

    • 通过反射, 能够在运行时取得程序或者程序中的每一个类型的成员活成成员的信息
    • 程序中的对象个别都是在编译时就确定下来,Java 反射机制能够动静地创建对象并且调用相干属性, 这些对象的类型在编译时是未知的
    • 也就是说 , 能够通过反射机制间接创建对象, 即便这个对象类型在编译时是未知的
  • Java 反射提供下列性能:

    • 在运行时判断任意一个对象所属的类
    • 在运行时结构任意一个类的对象
    • 在运行时判断任意一个类所具备的成员变量和办法, 能够通过反射调用 private 办法
    • 在运行时调用任意一个对象的办法

      反射的原理

  • 反射的外围: JVM 在运行时才动静加载类或者调用办法以及拜访属性, 不须要当时 (比方编译时) 晓得运行对象是什么
  • 类的加载:

    • Java 反射机制是围绕 Class 类开展的
    • 首先要理解类的加载机制:

      • JVM应用 ClassLoader 将字节码文件, 即 class文件加载到办法区内存中

        Class clazz = ClassLoader.getSystemClassLoader().loadClass("com.mypackage.MyClass");

        ClassLoader类依据类的齐全限定名加载类并返回一个 Class 对象

  • ReflectionData:

    • 为了进步反射的性能, 必须要提供缓存
    • class类外部应用一个 useCaches 动态变量来标记是否应用缓存
    • 这个值能够通过内部的 sun.reflect.noCaches 配置是否禁用缓存
    • class类外部提供了一个 ReflectionData 外部类用来寄存反射数据的缓存, 并申明了一个 reflectionData
    • 因为稍后进行按需提早加载并缓存, 所以这个域并没有指向一个实例化的 ReflectionData 对象

      // 标记是否应用缓存, 能够通过内部的 sun.reflect.noCaches 配置是否禁用缓存
      private static boolean useCaches = true;
      
      static class ReflectionData<T> {volatile Field[] declaredFields;
      volatile Field[] publicFields;
      volatile Method[] declaredMethods;
      volatile Method[] publicMethods;
      volatile Constructor<T>[] declaredConstructors;
      volatile Constructors<T>[] publicConstructors;
      volatile Field[] declaredPublicFields;
      volatile Method[] declaredPublicMethods;
      final int redefinedCount;
      
      ReflectionData(int redefinedCount) {this.redefinedCount = redefinedCount;}
      }
      
      // 这个是 SoftReference, 在内存资源缓和的时候可能会被回收
      // volatile 保障多线程环境下读写的正确性
       private volatile transient SoftReference<RefelectionData<T>> reflectionData;
      
      // 这个次要用于和 ReflectionData 中的 redefinedCount 进行比拟
      // 如果两个值不相等, 阐明 ReflectionData 缓存的数据曾经过期了
      private volatile transient classRedefinedCount = 0;

      反射的主要用途

  • 反射最重要的用处就是开发各种通用框架

    • 很多框架都是配置化的, 通过 XML 文件配置Bean
    • 为了保障框架的通用性, 须要依据配置文件加载不同的对象或者类, 调用不同的办法
    • 要使用反射, 运行时动静加载须要加载的对象
  • 示例:

    • 在使用 Struts 2 框架的开发中会在 struts.xml 中配置Action:

        <action name="login"
                 class="org.ScZyhSoft.test.action.SimpleLoginAction"
                 method="execute">
             <result>/shop/shop-index.jsp</result>
             <result name="error">login.jsp</result>
         </action>
  • 配置文件与 Action 建设映射关系
  • View 层发出请求时, 申请会被 StrutsPrepareAndExecuteFilter 拦挡
  • StrutsPrepareAndExecuteFilter会动静地创立 Action 实例

    • 申请login.action
    • StrutsPrepareAndExecuteFilter会解析 struts.xml 文件
    • 检索 actionnameloginAction
    • 依据 class 属性创立 SimpleLoginAction 实例
    • 应用 invoke 办法调用 execute 办法
  • 反射是各种容器实现的外围

    反射的使用

  • 反射相干的类在 StrutsPrepareAndExecuteFilter
  • 反射能够用于:

    • 判断对象所属的类
    • 取得 class 对象
    • 结构任意一个对象
    • 调用一个对象
  • 九大预约义的 Class 对象:

    • 根本的 Java 类型: boolean, byte, char, short, int, long, float, double
    • 关键字 void 通过 class 属性的也示意为 Class 对象
    • 包装类或者 void 类的动态 TYPE 字段:

      • Integer.TYPE == int.class
      • Integer.class == int.class
    • 数组类型的 Class 实例对象:

      • Class<String[]> clz = String[].class;
      • 数组的 Class 对象如何比拟是否相等:

        • 数组的维数
        • 数组的类型
        • Class 类中的isArray(), 用来判断是否示意一个数组类型

          取得 Class 对象

  • 应用 Class 类的 forName 静态方法:

    public static Class<?> forName(String className);
    
    
    
    /* 在 JDBC 中应用这个办法加载数据库驱动 */
    Class.forName(driver);
  • 间接获取一个对象的 class:

    Class<?> klass=int.class;
    Class<?> classInt=Integer.TYPE;
  • 调用对象的 getClass()办法:

    StringBuilder str=new StringBuilder("A");
    Class<?> klass=str.getClass();

    判断是否是某个类的实例

  • 一般来说, 应用 instanceof 关键字判断是否为某个类的实例
  • 在反射中, 能够应用 Class 对象的 isInstance() 办法来判断是否为某个类的实例, 这是一个native 办法

    public native boolean isInstance(Object obj);

    创立实例

    通过反射生成对象的实例次要有两种形式:

  • 应用 Class 对象的 newInstance()办法来创立 Class 对象对应类的实例:

    Class<?> c = String.class;
    Object str = c.newInstance();
  • 先通过 Class 对象获取指定的 Constructor 对象, 再调用 Constructor 对象的 newInstance()办法来创立实例: 能够用指定的结构器结构类的实例

    /* 获取 String 所对应的 Class 对象 */
    Class<?> c=String.class;
    
    /* 获取 String 类带一个 String 参数的结构器 */
    Constructor constructor=c.getConstructor(String.class);
    
    /* 依据结构器创立实例 */
    Object obj=constructor.newInstance("abc");
    System.out.println(obj);

    获取办法

    获取 Class 对象的办法汇合, 次要有三种办法:

  • getDeclaredMethods(): 返回类或接口申明的所有办法:

    • 包含公共, 爱护, 默认 (包) 拜访和公有办法
    • 不包含继承的办法

      public Method[] getDeclaredMethods() throws SecurityException {}
  • getMethods(): 返回某个类所有的 public 办法

    • 包含继承类的 public 办法

      public Method[] getMethods() throws SecurityException {}
  • getMethod(): 返回一个特定的办法

    • 第一个参数 : 办法名称
    • 前面的参数 : 办法的参数对应 Class 的对象

      public Method getMethod(String name,Class<?>... parameterType) {}
  • 获取办法示例:

    public class MethodTest {public static void methodTest() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
          Class<?> c = methodClass.class;
          Object object = c.newInstance();
          Method[] methods = c.getMethods();
          Method[] declaredMethods = c.getDeclaredMethods();
          // 获取 methodClass 类中的 add 办法
          Method method = c.getMethod("add", int.class, int.class);
          // getMethods()办法获取的所有办法
          System.out.println("getMethods 获取的办法:");
          for (Method m:methods)
              System.out.println(m);
          // getDeclaredMethods()办法获取的所有办法
          System.out.println("getDeclaredMethods 获取的办法:");
          for (Method m:declaredMethods)
              System.out.println(m);
      }
    }
    
    class methodClass {
      public final int n = 3;
      
      public int add(int a, int b) {return a + b;}
      
      public int sub(int a, int b) {return a * b;}
    }

    程序运行后果:

    getMethods 获取的办法:public int org.ScZyhSoft.common.methodClass.add(int,int)
    public int org.ScZyhSoft.common.methodClass.sub(int,int)
    public final void java.lang.Object.wait() throws java.lang.InterruptedException
    public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
    public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
    public boolean java.lang.Object.equals(java.lang.Object)
    public java.lang.String java.lang.Object.toString()
    public native int java.lang.Object.hashCode()
    public final native java.lang.Class java.lang.Object.getClass()
    public final native void java.lang.Object.notify()
    public final native void java.lang.Object.notifyAll()
    getDeclaredMethods 获取的办法:public int org.ScZyhSoft.common.methodClass.add(int,int)
    public int org.ScZyhSoft.common.methodClass.sub(int,int)

    通过getMethods() 获取的办法能够获取到父类的办法

    获取结构器信息

  • 通过 Class 类的 getConstructor 办法失去 Constructor 类的一个实例
  • Constructor类中 newInstance 办法能够创立一个对象的实例:

    public T newInstance(Objec ... initargs)

    newInstance办法能够依据传入的参数来调用对应的 Constructor 创建对象的实例

    获取类的成员变量信息

  • getFileds: 获取私有的成员变量
  • getDeclaredFields: 获取所有已申明的成员变量, 然而不能失去父类的成员变量

    调用办法

  • 从类中获取一个办法后, 能够应用invoke() 来调用这个办法

    public Object invoke(Object obj, Object ... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {}
  • 示例:

    public class InvokeTest {public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
          Class<?> klass = method.class;
          // 创立 methodClass 的实例
          Object obj = klass.newInstance();
          // 获取 methodClass 的 add 办法
          Method method = klass.getMethod("add", int.class, int.class);
          // 调用 method 对应的办法, 实现 add(1,4)
          Object result = method.invoke(obj, 1, 4);
          System.out.println(result);
      }
    }
    
    class methodClass {
      public final int n = 3;
      public int add(int a, int b) {return a + b;}
      public int sub(int a,int b) {return a * b;}
    }

    利用反射创立数组

  • 数组是 Java 中一种非凡的数据类型, 能够赋值给一个Object Reference
  • 利用反射创立数组的示例:

    public static void ArrayTest() throws ClassNotFoundException {Class<?> cls = class.forName("java.lang.String");
      Object array = Array.newInstance(cls, 25);
      // 在数组中增加数据
      Array.set(array, 0, "C");
      Array.set(array, 1, "Java");
      Array.set(array, 2, "Python");
      Array.set(array, 3, "Scala");
      Array.set(array, 4, "Docker");
      // 获取数据中的某一项内容
      System.out.println(Array.get(array, 3));
    }

    Array类是 java.lang.reflect.Array 类, 通过Array.newInstance() 创立数组对象:

    public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException {return newArray(componentType, length);
    }

    newArray办法是一个 native 办法, 具体实现在 HotSpot JVM 中, 源码如下:

    private static native Object newArray(Class<?> componentType, int length) throws NegativeArraySizeException;
    
    
    ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    
  • newArray 源码目录: openjdk\hotspot\src\share\vm\runtime\reflection.cpp

    arrayOop Reflection::reflect_new_array(oop element_mirror, jint length, TRAPS) {
    if (element_mirror == NULL) {
    THROW_0(vmSymbols::java_lang_NullPointerException());
    }
    if (length < 0) {
    THROW_0(vmSymbols::java_lang_NegativeArraySizeException());
    }
    if (java_lang_Class::is_primitive(element_mirror)) {
    Klass* tak = basic_type_mirror_to_arrayklass(element_mirror, CHECK_NULL);
    return TypeArrayKlass::cast(tak)->allocate(length, THREAD);
    } else {
    Klass* k = java_lang_Class::as_Klass(element_mirror);
    if (k->oop_is_array() && ArrayKlass::cast(k)->dimension() >= MAX_DIM) {

    THROW_0(vmSymbols::java_lang_IllegalArgumentException());

    }
    return oopFactory::new_objArray(k, length, THREAD);
    }
    }

  • Array类的 setget办法都是 native 办法, 具体实现在 HotSpot JVM 中, 对应关系如下:

    • set: Reflection::array_set
    • get: Reflection::array_get

      invoke 办法

  • 在 Java 中很多办法都会调用 invoke 办法, 很多异样的抛出多会定位到 invoke 办法:

    java.lang.NullPointerException
    at ......
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)

    invoke 执行过程

  • invoke办法用来在运行时动静地调用某个实例的办法, 实现如下:

    @CallSensitive
    public Object invoke(Object obj, Object ... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {if (!override) {if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {Class<?> caller = Refelection.getCallerClass();
              checkAccess(caller, clazz, obj, modifiers);
          }
      }
      MethodAccessor ma = methodAccessor;
      if (ma == null) {ma = acquireMethodAccessor();
      }
      return ma.invoke(obj, args);
    }

    权限查看

  • AccessibleObject类是 Field,MethodConstructor对象的基类:

    • 提供将反射的对象标记为在应用时勾销默认 Java 语言访问控制查看的能力
  • invoke办法会首先查看 AccessibleObjectoverride属性的值:

    • override 默认值为 false:

      • 示意须要权限调用规定, 调用办法时须要查看权限
      • 也能够应用setAccessible() 设置为true
    • override 如果值为 true:

      • 示意疏忽权限规定, 调用办法时无需查看权限
      • 也就是说, 能够调用任意 private 办法, 违反了封装
  • 如果 override 属性为默认值 false, 则进行进一步的权限查看:
  • 首先用 Reflection.quickCheckMemberAccess(clazz, modifiers) 办法查看办法是否为public
    1.1 如果是 public 办法的话, 就跳过本步
    1.2 如果不是 public 办法的话, 就用 Reflection.getCallerClass() 办法获取调用这个办法的 Class 对象, 这是一个 native 办法

    @CallerSensitive
      public static native Class<?> getCallerClass();
    
    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    
  • 在 OpenJDK 中能够找到 getCallerClass 办法的 JNI 入口 -Reflection.c

    JNIEXPORT jclass JNICALL Java_sun_reflect_Reflection_getCallerClass__
    (JNIEnv *env, jclass unused)
    {
    return JVM_GetCallerClass(env, JVM_CALLER_DEPTH);
    }


  • JVM_GetCallerClass 的源码位于 jvm.cpp 中

    VM_ENTRY(jclass, JVM_GetCallerClass(JNIEnv* env, int depth))
    JVMWrapper(“JVM_GetCallerClass”);
    // Pre-JDK 8 and early builds of JDK 8 don’t have a CallerSensitive annotation; or
    // sun.reflect.Reflection.getCallerClass with a depth parameter is provided
    // temporarily for existing code to use until a replacement API is defined.
    if (SystemDictionary::reflect_CallerSensitive_klass() == NULL || depth != JVM_CALLER_DEPTH) {
    Klass* k = thread->security_get_caller_class(depth);
    return (k == NULL) ? NULL : (jclass) JNIHandles::make_local(env, k->java_mirror());
    }
    // Getting the class of the caller frame.
    //
    // The call stack at this point looks something like this:
    //
    // [0] [@CallerSensitive public sun.reflect.Reflection.getCallerClass]
    // [1] [@CallerSensitive API.method]
    // [.] [(skipped intermediate frames) ]
    // [n] [caller]
    vframeStream vfst(thread);
    // Cf. LibraryCallKit::inline_native_Reflection_getCallerClass
    for (int n = 0; !vfst.at_end(); vfst.security_next(), n++) {
    Method* m = vfst.method();
    assert(m != NULL, “sanity”);
    switch (n) {
    case 0:

    // This must only be called from Reflection.getCallerClass
    if (m->intrinsic_id() != vmIntrinsics::_getCallerClass) {THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), "JVM_GetCallerClass must only be called from Reflection.getCallerClass");
    }
    // fall-through

    case 1:

    // Frame 0 and 1 must be caller sensitive.
    if (!m->caller_sensitive()) {THROW_MSG_NULL(vmSymbols::java_lang_InternalError(), err_msg("CallerSensitive annotation expected at frame %d", n));
    }
    break;

    default:

    if (!m->is_ignored_by_security_stack_walk()) {
      // We have reached the desired frame; return the holder class.
      return (jclass) JNIHandles::make_local(env, m->method_holder()->java_mirror());
    }
    break;

    }
    }
    return NULL;
    JVM_END

  • 获取 Class 对象 caller 后应用 checkAccess 办法进行一次疾速的权限校验 ,checkAccess办法实现如下:

    volatile Object securityCheckCache;
    
      void checkAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers) throws IllegalAccessException {if(caller == clazz){    // 疾速校验
              return;                // 权限通过校验
          }
          Object cache = securityCheckCache;    // 读取 volatile
          Class<?> targetClass = clazz;
          if (obj != null && Modifier.isProtected(modifiers) && ((targetClass = obj.getClass()) != clazz)) {    // 必须匹配 caller,targetClass 中的一个
              if (cache instanceof Class[]) {Class<?>[] cache2 = (Class<?>[]) cache;
                  if (cache2[1] == targetClass && cache[0] == caller) {return;        // 校验通过}
              }
          } else if (cache == caller) {return;                // 校验通过}
          slowCheckMemberAccess(caller, clazz, obj, modifiers, targetClass);
      }

    首先先执行一次疾速校验, 一旦 Class 正确则权限查看通过; 如果未通过, 则创立一个缓存, 两头再进行查看

  • 如果下面所有的权限查看都未通过, 将会执行更具体的查看:

    void slowCheckMemberAccess(Class<?> caller, Class<?> clazz, Object obj, int modifiers, Class<?> targetClass) throws IllegalAccessException {Refelection.ensureMemberAccess(caller, clazz, obj, modifiers);
      // 如果胜利, 就更新缓存
      Object cache = ((targetClass == clazz) ? caller : new Class<?>[] {caller, targetClass});
      securityCheckCache = cache;
    }

    Reflection.ensureMemberAccess 办法持续查看权限. 若查看通过就更新缓存, 这样下一次同一个类调用同一个办法时就不必执行权限查看了, 这是一种简略的缓存机制
    因为 JMMhappens-before规定可能保障缓存初始化可能在写缓存之间产生, 因而两个 cache 不须要申明为 volatile

  • 查看权限的工作到此结束. 如果没有通过查看就会抛出异样, 如果通过查看就会到下一步

    调用 MethodAccessor 的 invoke 办法

  • Method.invoke() 不是本身实现反射调用逻辑, 而是通过 sun.refelect.MethodAccessor 来解决
  • Method 对象的根本形成:

    • 每个 Java 办法有且只有一个 Method 对象作为root, 相当于根对象, 对用户不可见
    • 当创立 Method 对象时, 代码中取得的 Method 对象相当于其正本或者援用
    • root对象持有一个 MethodAccessor 对象, 所有获取到的 Method 对象都共享这一个 MethodAccessor 对象
    • 必须保障 MethodAccessor 在内存中的可见性
  • root 对象及其申明:

    private volatile MethodAccessor methodAccessor;
    /**
     * For sharing of MethodAccessors. This branching structure is
     * currently only two levels deep (i.e., one root Method and
     * potentially many Method objects pointing to it.)
     * 
     * If this branching structure would ever contain cycles, deadlocks can
     * occur in annotation code.
     */
    private Method  root;
  • MethodAccessor:

    /**
     * This interface provides the declaration for
     * java.lang.reflect.Method.invoke(). Each Method object is
     * configured with a (possibly dynamically-generated) class which
     * implements this interface
     */
     public interface MethodAccessor {// Matches specification in {@link java.lang.reflect.Method}
       public Object invoke(Object obj, Object[] args) throws IllegalArgumentException, InvocationTargetException;
     }

    MethodAccessor是一个接口, 定义了 invoke() 办法, 通过Usage 能够看出 MethodAccessor 的具体实现类:

  • sun.reflect.DelegatingMethodAccessorImpl
  • sun.reflect.MethodAccessorImpl
  • sun.reflect.NativeMethodAccessorImpl
  • 第一次调用 Java 办法对应的 Method 对象的 invoke() 办法之前, 实现调用逻辑的 MethodAccess 对象还没有创立
  • 第一次调用时, 才开始创立 MethodAccessor 并更新为root, 而后调用MethodAccessor.invoke() 实现反射调用

    /**
     * NOTE that there is no synchronization used here. 
     * It is correct(though not efficient) to generate more than one MethodAccessor for a given Method.
     * However, avoiding synchronization will probably make the implementation more scalable.
     */
    
    private MethodAccessor acquireMethodAccessor() {
      // First check to see if one has been created yet, and take it if so
      MethodAccessor tmp = null;
      if (root != null)
          tmp = root.getMethodAccessor();
      if (tmp != null) {methodAccessor = tmp;} else {tmp = reflectionFactory.newMethodAccessor(this);
          setMethodAccessor(tmp);
      } 
      return tmp;
    } 
  • methodAccessor实例由 reflectionFactory 对象操控生成 ,reflectionFactory是在 AccessibleObject 中申明的:

    /**
     * Reflection factory used by subclasses for creating field,
     * method, and constructor accessors. Note that this is called very early in the bootstrapping process.
     */
    static final ReflectionFactory reflectionFactory = AccessController.doPrivileged(new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
  • sun.reflect.ReflectionFactory办法:

    public class ReflectionFactory {
      private static boolean initted = false;
      private static Permission reflectionFactoryAccessPerm = new RuntimePermission("reflectionFactoryAccess");
      private static ReflectionFactory soleInstance = new ReflectionFactory();
      // Provides access to package-private mechanisms in java.lang.reflect
      private static volatile LangReflectAccess langReflectAccess;
    
      /**
       * "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 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 Constructors and then switch to the bytecode-based implementations
       */
    
      // Package-private to be accessible to NativeMethodAccessorImpl and NativeConstructorAccessorImpl
      private static noInflation = false;
      private static int inflationThreshold = 15;
    
      // 生成 MethodAccessor
      public MethodAccessor newMethodAccessor(Method method) {checkInitted();
    
          if (noInflation && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {return new MethodAccessorGenerator().generateMethod(method.getDeclaringClass(),
                                           method.getName(),
                                           method.getParameterTypes(),
                                           method.getReturnType(),
                                           method.getExceptionTypes(),
                                           method.getModifiers());
          } else {NativeMethodAccessorImpl acc = new NativeMethodAccessorImpl(method);
              DelegatingMethodAccessorImpl res = new DelegatingMethodAccessorImpl(acc);
              acc.setParent(res);
              return res;
          }
      }
    
      /**
       * We have to defer full initialization of this class until after the static initializer is run since java.lang.reflect
       * Method's static initializer (more properly, that for java.lang.reflect.AccessibleObject) causes this class's to be run, before the system properties are set up
       */
       private static void checkInitted() {if (initted) return;
           AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void 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 these 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.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 (NumberFormatException e) {throw new RuntimeException("Unable to parse property sun.reflect.inflationThreshold", e);
                          }
                      }
                      initted = true;
                      return null;
                  }
              });
      }
    }
  • 理论的 MethodAccessor 实现有两个版本, 一个是 Java 版本, 一个是 native 版本, 两者各有特点:

    • 首次启动时Method.invoke()Constructor.newInstance() 办法采纳 native 办法要比 Java 办法快 3 - 4 倍
    • 启动后 native 办法又要耗费额定的性能而慢于 Java 办法
    • Java 实现的版本在初始化时须要较多工夫, 但短暂来说性能较好
  • 这是 HotSpot 的优化形式带来的性能个性:

    • 逾越 native 边界会对优化有妨碍作用
  • 为了尽可能地缩小性能损耗,HotSpot JDK 采纳 inflation 形式:

    • Java 办法在被反射调用时, 结尾若干次应用 native 版
    • 等反射调用次数超过阈值时则生成一个专用的 MethodAccessor 实现类, 生成其中的invoke() 办法的字节码
    • 当前对该 Java 办法的反射调用就会应用 Java 版本
  • ReflectionFactory.newMethodAccessor() 生成 MethodAccessor 对象的逻辑:

    • native版开始会会生成 NativeMethodAccessorImplDelegatingMethodAccessorImpl两个对象
  • DelegatingMethodAccessorImpl:

    /* Delegates its invocation to another MethodAccessorImpl and can change its delegate at run time */
    class DelegatingMethodAccessorImpl extends MethodAccessorImpl {
      private MethodAccessorImpl delegate;
      DelegatingMethodAccessorImpl(MethodAccessorImpl delegate) {setDelegate(delegate);
      }
      public Object invoke(Object obj, Object[] args)
          throws IllegalArgumentException, InvocationTargetException
      {return delegate.invoke(obj, args);
      }
      void setDelegate(MethodAccessorImpl delegate) {this.delegate = delegate;}
    }

    DelegatingMethodAccessorImpl对象是一个中间层, 不便在 native 版与 Java 版的 MethodAccessor 之间进行切换

  • native 版 MethodAccessor 的 Java 方面的申明: sun.reflect.NativeMethodAccessorImpl

    /* Used only for the first few invocations of a Method; afterward,switches to bytecode-based implementation */
    class NativeMethodAccessorImpl extends MethodAccessorImpl {
      private Method method;
      private DelegatingMethodAccessorImpl parent;
      private int numInvocations;
      NativeMethodAccessorImpl(Method method) {this.method = method;}
      public Object invoke(Object obj, Object[] args)
          throws IllegalArgumentException, InvocationTargetException
      {
          /* We can't inflate methods belonging to vm-anonymous classes because that kind of class can't be referred to by name, hence can't be found from the generated bytecode */
          if (++numInvocations > ReflectionFactory.inflationThreshold()
                  && !ReflectUtil.isVMAnonymousClass(method.getDeclaringClass())) {MethodAccessorImpl acc = (MethodAccessorImpl)
                  new MethodAccessorGenerator().
                      generateMethod(method.getDeclaringClass(),
                                     method.getName(),
                                     method.getParameterTypes(),
                                     method.getReturnType(),
                                     method.getExceptionTypes(),
                                     method.getModifiers());
              parent.setDelegate(acc);
          }
          return invoke0(method, obj, args);
      }
      void setParent(DelegatingMethodAccessorImpl parent) {this.parent = parent;}
      private static native Object invoke0(Method m, Object obj, Object[] args);
    }
  • 每次NativeMethodAccessorImpl.invoke() 办法被调用时, 程序调用计数器都会减少1, 看看是否超过阈值
  • 如果超过则调用 MethodAccessorGenerator.generateMethod() 来生成Java 版的 MethodAccessor 的实现类
  • 扭转 DelegatingMethodAccessorImpl 所援用的 MethodAccessorJava
  • 经由 DelegatingMethodAccessorImpl.invoke() 调用到的就是Java 版的实现

    JVM 层 invoke0 办法

  • invoke0办法是一个 native 办法, 在 HotSpot JVM 里调用 JVM_InvokeMethod 函数:

    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:

    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)) {
          // Only for primitive type vm allocates memory for java object.
          // See box() method.
          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

    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);
    }

    Java 的对象模型 :klassoop

    Java 版的实现

  • JavaMethodAccessor 的生成应用 MethodAccessorGenerator 实现

      Generator for sun.reflect.MethodAccessor and
      sun.reflect.ConstructorAccessor objects using bytecodes to
      implement reflection. A java.lang.reflect.Method or
      java.lang.reflect.Constructor object can delegate its invoke or
      newInstance method to an accessor using native code or to one
      generated by this class. (Methods and Constructors were merged
      together in this class to ensure maximum code sharing.)

    使用了 asm 动静生成字节码技术 – sun.reflect.ClassFileAssembler

    invoke 总结

  • invoke 办法的过程:
  • MagicAccessorImpl:

    • 本来 Java 的平安机制使得不同类之间不是任意信息都可见, 但 JDK 外面专门设了个 MagicAccessorImpl 标记类开了个后门来容许不同类之间信息能够相互拜访, 由 JVM 治理

      /** <P> MagicAccessorImpl (named for parity with FieldAccessorImpl and
      others, not because it actually implements an interface) is a
      marker class in the hierarchy. All subclasses of this class are
      "magically" granted access by the VM to otherwise inaccessible
      fields and methods of other classes. It is used to hold the code
      for dynamically-generated FieldAccessorImpl and MethodAccessorImpl
      subclasses. (Use of the word "unsafe" was avoided in this class's
      name to avoid confusion with {@link sun.misc.Unsafe}.) </P>
      <P> The bug fix for 4486457 also necessitated disabling
      verification for this class and all subclasses, as opposed to just
      SerializationConstructorAccessorImpl and subclasses, to avoid
      having to indicate to the VM which of these dynamically-generated
      stub classes were known to be able to pass the verifier. </P>
      <P> Do not change the name of this class without also changing the
      VM's code. </P> */
      class MagicAccessorImpl {}
  • @CallerSensitive 注解

    Summary: Improve the security of the JDK’s method-handle implementation by replacing the existing
     hand-maintained list of caller-sensitive methods with a mechanism that accurately identifies
    such methods and allows their callers to be discovered reliably.
    /**
     * A method annotated @CallerSensitive is sensitive to its calling class,
     * via {@link sun.reflect.Reflection#getCallerClass Reflection.getCallerClass},
     * or via some equivalent.
     *
     * @author John R. Rose
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({METHOD})
    public @interface CallerSensitive {}
  • @CallerSensitive注解润饰的办法从一开始就晓得具体调用此办法的对象

    • 不必再通过一系列的查看就能确定具体调用此办法的对象
    • 实际上是调用 sun.reflect.Reflection.getCallerClass 办法
  • Reflection类位于调用栈中的 0 帧地位

    • sun.reflect.Reflection.getCallerClass() 办法返回调用栈中从 0 帧开始的第 x 帧中的类实例
    • 该办法提供的机制可用于确定调用者类, 从而实现 ” 感知调用者(Caller Sensitive)” 的行为
    • 即容许应用程序依据调用类或调用栈中的其它类来扭转其本身的行为

      反射留神点

  • 反射会额定耗费系统资源, 如果不须要动静地创立一个对象, 就不要应用反射
  • 反射调用办法时能够疏忽权限查看. 可能会毁坏封装性而导致平安问题
退出移动版