美文网首页
反射支撑

反射支撑

作者: Wi1ls努力努力再努力 | 来源:发表于2019-06-13 16:23 被阅读0次

    以下讨论的都是Dalvik虚拟机,以 Android4.0.4 为基础


    关于动态代理参见Dalvik 动态代理的实现

    以无参构造为例

    //@Class.java
    public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException {
      return (Constructor) getConstructorOrMethod("<init>", false, true, parameterTypes);
    }
    
    /**
    *  Returns a constructor or method with the specified name.
    *
    *  @params name the method name, or "<init>" to return a constructor.
    *  @param recursive true to search supertypes;
    */
    private Member getConstructorOrMethod(String name, boolean recursive, boolean publicOnly, Class<?>[] parameterTypes) throws NoSuchMethodException {
      ...
      Member result = recursive ? getPublicConstructorOrMethodRecursive(name, parameterTypes)
            : Class.getDeclaredConstructorOrMethod(this, name, parameterTypes);
      ...
    }
    

    从上面的说明可以看出来,在 4.0.4下,构造函数是视为函数名为"<init>"的特殊的函数。其反射获取和普通函数一样,传入函数名("<init>")和参数类型(Class<?>[ ] parameterTypes);同时 recursive 表示递归查询父类,在此处为 false,因此最终调用者为

    static native Member getDeclaredConstructorOrMethod(Class clazz, String name, Class[ ] args);
    

    接着来到 ./dalvik/vm/native/java_lang_Class.cpp 进入 native 层。

    static void Dalvik_java_Class_getDeclaredConstructorOrMethod(const u4* args, JValue* pResult){
      ClassObject* clazz = (ClassObject*) args[0];
      StringObject* nameObject = (StringObject*) args[1];
      ArrayObject* methodArgs = (ArrayObject*) args[2];
      
      Object* methodObject;
      methodObject = dvmGetDeclaredConstructorOrMethod(clazz, nameObject, methodArgs);
      dvmReleaseTrackedAlloc(methodObj, NULL);
      
      RETURN_PTR(methodObj);
    }
    

    于是寻找目标方法的任务就转交给了 dvmGetDeclaredConstructorOrMethod( ) @Reflect.cpp

    //./dalvik/vm/reflect/Reflect.cpp
    Object* dvmGetDeclaredConstructorOrMethod(ClassObject* clazz, StringObject* nameObj, ArrayObject* args){
      Object* result = NULL;
      DexStringCache targetDescriptorCache;
      char* name;
      const char* targetDescriptor; 
      dexStringCacheInit(&targetDecriptorCache);
      
      name = dvmCreateCstrFromString(nameObj);
      createTargetDescriptor(args, &targetDescriptorCache);
      targetDescriptor = targetDescriptorCache.value;
      
      result = findConstructorOrMethodInArray(clazz->directMethodCount, clazz->directMethods, name, targetDescriptor);
      if(result == NULL){
        result = findConstructorOrMethodInArray(clazz->virtualMethodCount, clazz->virtualMethods, name, targetDescriptor);
      }
    }
    

    很明显,在寻找目标方法的过程中,优先从 directMethod 中寻找,其次再考虑从 virtualMethods中寻找

    static Object* findConstructorOrMethodInArrag(int methodsCount, Method* methods, const char* name, const char* parameterDescriptors){
      Method* method = NULL; 
      Method* result = NULL;
      int i;
      for(i = 0; i < methodsCount; i++){
        method = &methods[i];
        if(strcmp(name, method->name) != 0
            || dvmIsMirandaMethod(method)
            || dexProtoCpmpareToParameterDesriptors(&method->prototype, parameterDescriptors) != 0) {
          continue;
        }
        result = method;
    
        if(!dvmIsSyntheticMethod(method){
          break;
        }
      }
      if(result != NULL){
        return dvmCreateReflectObjForMethod(result->clazz, result);
      }
      return NULL;
    }
    

    在从 directMethods 和 virtualMethods 中寻找目标方法的过程中,先判断方法名,判断是否是 Miranda 方法,然后再判断参数是否一致。
    可以看到整个方法反射解析的过程依赖于对于 Dex 中类的解析为 ClassObject 的过程。另外,虽然父类的一些方法被定为 virtual 方法是可以被子类继承的,但是如果子类没有显式得重写,那么改方法便不会进入子类的 ClassRef 的 virtualMethods 中,于是用子类去 getDeclaredMethod(String methodName)是找不到这个方法的。只能用父类去查找。一旦子类重写了,那么该方法便会进入子类的 virtualMethods 表中。
    那为什么子类可以显式调用父类的这个方法呢,反编译后可以发现该方法的code的操作码是invoke-virtual。在 Android 4.0.4下的 Dalvik 的protable 解释器下,invoke-virtual下的 method 寻找有以下路径;

    0. @ InterpC-portable.cpp 下的 invokevirtual 的解释
    1.dvmDexGetResolvedMethod(methodClassDex, ref)@DvmDex.h, 如果找不到 则进入 2.
    2.dvmResolveMethod(curMethod->clazz, ref, METHOD_VIRTUAL)@Resolve.cpp;
    3.由于参数是 METHOD_VIRTUAL,于是 dvmFindVirtualMethodHier(resClass, name,&proto)@Object.cpp
    4. findMethodInListByDescriptor(clazz, true, true, methodName, descriptor)@Object.cpp
    5.根据传进入的参数,会依次向上遍历父类,直到找到该 virtual 为止
    

    可以看到,在反射寻找virtual方法和 invoke-virtual 的不同,前者只会在该 ClassRef 的 virtualMethods 中寻找,后者会向上遍历父类去寻找。


    字段的寻找

    //@Class.java
    public Field getDeclaredField(String name) throws NoSuchFieldException{
      ...
      Field result = getDeclaredField(this, name);
      ...
      return result;
    }
    
    //@java_lang_Class.cpp
    static void Dalvik_java_lang_Class_getDeclaredField(const u4* args, JValue* pResult){
      ClassObject* clazz = (ClassObject*) args[0];
      StringObject* nameObj = (StringObject*) args[1];
      Object* fieldObject = dvmGetDeclaredField(clazz, nameObject);
      dvmReleaseTrackedAlloc((Object*) fieldObject, NULL);
      RETURN_PTR(fieldObj);
    }
    
    //@Reflect.cpp
    Object* dvmGetDeclaredField(ClassObject* clazz, StringObject* nameObj){
      int i;
      Object* fieldObj = NULL;
      char* name = dvmCreateCstrFromString(nameObj);
      
      if(!dvmIsClassInitialized(gDvm.classJavaLangReflectField))
        dvmInitClass(gDvm.classJavaLangReflectField);
    
      for(i = 0; i < class.sfieldCount; i++){
        Field* field = &clazz->sfields[i];
        if(strcmp(name, field->name) == 0){
          fieldObj =createFieldObject(field, clazz);
          break;
        }
      }
    
      if(fieldObj == NULL){
        for(i = 0; i< clazz->ifieldCount; i++){
          Field* field = &clazz->ifields[i];
          if(strcmp(name, field->name) ==0){
            fieldObj = createFieldObject(field, clazz);
            break;
          }
        }
      }
    }
    free(name);
     return fieldObj;
    

    在 classObject 中会优先从 static 的字段寻找,再去寻找成员字段。
    同时也说明了getDeclaredField( )会从改类的所有字段去匹配。


    我们知道getField( )只会去匹配目标类及其父类的 public 字段,来看下实现原理

    //@Class.java
    public Field getField(String name) throws NoSuchFieldException {
            if (name == null) {
                throw new NullPointerException("name == null");
            }
            Field result = getPublicFieldRecursive(name);
            if (result == null) {
                throw new NoSuchFieldException(name);
            }
            return result;
        }
    
        private Field getPublicFieldRecursive(String name) {
            // search superclasses
            for (Class<?> c = this; c != null; c = c.getSuperclass()) {
                Field result = Class.getDeclaredField(c, name);
                if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
                    return result;
                }
            }
    
            // search implemented interfaces
            for (Class<?> c = this; c != null; c = c.getSuperclass()) {
                for (Class<?> ifc : c.getInterfaces()) {
                    Field result = ifc.getPublicFieldRecursive(name);
                    if (result != null && (result.getModifiers() & Modifier.PUBLIC) != 0) {
                        return result;
                    }
                }
            }
    
            return null;
        }
    

    从源码也可以看出,会依次从本类在父类循环遍历寻找 public 的字段,如果没有的话就循环遍历本类及其父类的实现接口。
    于是优先顺序 本类字段->父类字段->父类的父类字段->...->本类实现接口->父类实现接口->父类的父类实现接口->...(均为 public 描述)


    同理,在获取方法的时候都会调用到

    private Member getConstructorOrMethod(String name, boolean recursive,
                boolean publicOnly, Class<?>[ ] parameterTypes);
    

    关键参数有 boolean recursive, boolean publicOnly;

    • 对于 getDeclaredMethod( ) ,recursive = false, publicOnly = false, 说明该方法在指定 class 所有方法寻找,而不遍历父类。

    • 对于 getMethod( ), recursive = true, publicOnly = true, 说明该方法会向上遍历父类并且只匹配 public 方法。

    • 对于 getConstructor( ), recursive = false, publicOnly = true.


    • 关于 invoke( )@Method;
    //@Method.java
    public Object invoke(Object receiver, Object... args){
      if(args == null){
        args = EmptyArrag.OBJECT;
      }
      return invokeNative(receiver, args, declaringClass, parameterTypes, returnType, slot, flag);
    }
    
    private native Object invokeNative(Object obj, Object[ ] args, Class<?> declaringClass, Class<?> paramterTypes, Class<?> returnType, int slot, boolean noAccessCheck);
    

    先来看参数:

    • Object obj : 调用改方法的对象,如果是 static 则为 Null
    • Object[ ] args : 具体的参数
    • Class<?> declaringClass : 方法所属的类
    • Class<?>[ ] parameterTypes : 参数列表
    • Class<?> returnType : 对象
    • int slot : 方法在类方法表中的序号
    • boolean noAccessCheck : 是否检查权限(true 表示不检查)

    其中的Object obj, Object[ ] args 是由外部传入的方法参数; Class<?> declaringClass, Class<?>[ ] parameterTypes, Class<?> returnType, int slot 是在 native 层创建 Method 对象的时候确定下来的; 而 boolean noAccessCheck 默认是 false,即默认检查,在检查的情况下,比如方法的 private 就不允许 invoke,抛异常。
    现在来看 native 层确定下来的参数。

    //Reflect.cpp
    Object* dvmCreateReflectMethodObject(const Method* meth){
      Object* result = NULL;
      ArrayObject* params = NULL;
      ArrayObject* exceptions = NULL;
      StringObject* exceptions = NULL;
      StringObject* nameObj = NULL;
      Object* methObj;
      ClassObject* returnType;
      DexStringCache mangle;
      char* cp;
      int slot;
      
      dexStringCacheInit(&mangle);
    
      methodObj = dvmAllocObject(gDvm.classJavaLangReflectMethod);
    
      cp = dvmCopyDescriptorStringFromMethod(meth, &mangle);
      params = convertSignatureToClassArray(&cp, meth->clazz);
      
      cp++;
      returnType = convertSignaturePartToClass(&cp, meth->clazz);
    
      exceptions = dvmGetMethodThrows(meth);
    
      nameObj = dvmCreateStringFromCstr(meth->name);
    
      slot = methodToSlot(meth);
    
      JValue unused;
      dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectMethod_init, methObj, &unused, meth->clazz, params, exceptions, returnType, nameObj, slot);
    
      result = methObj;
    }
    

    我们来看 gDvm.methJavaLangReflectMethod_init,在 ./dalvik/vm/InitRefs.cpp中定义

    static bool initConstructorReferences() {
        static struct { Method** method; const char* name; const char* descriptor; } constructors[] = {
            { &gDvm.methJavaLangStackTraceElement_init, "Ljava/lang/StackTraceElement;",
              "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V" },
            { &gDvm.methJavaLangReflectConstructor_init, "Ljava/lang/reflect/Constructor;",
              "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;I)V" },
            { &gDvm.methJavaLangReflectField_init, "Ljava/lang/reflect/Field;",
              "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;I)V" },
            { &gDvm.methJavaLangReflectMethod_init, "Ljava/lang/reflect/Method;",
              "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;"
              "Ljava/lang/String;I)V" },
            { &gDvm.methJavaNioReadWriteDirectByteBuffer_init, "Ljava/nio/ReadWriteDirectByteBuffer;",
              "(II)V" },
            { &gDvm.methOrgApacheHarmonyLangAnnotationAnnotationMember_init,
              "Lorg/apache/harmony/lang/annotation/AnnotationMember;",
              "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V" },
            { NULL, NULL, NULL }
        };
    
        int i;
        for (i = 0; constructors[i].method != NULL; i++) {
            if (!initDirectMethodReference(constructors[i].method, constructors[i].name,
                    "<init>", constructors[i].descriptor)) {
                return false;
            }
        }
    
        return true;
    }
    

    可见gDvm.methJavaLangReflectMethod_init映射到了 Java 层 Method.java 中的构造方法。

     private Method(Class<?> declaring, Class<?>[] paramTypes, Class<?>[] exceptTypes, Class<?> returnType, String name, int slot)
        {
            this.declaringClass = declaring;
            this.name = name;
            this.slot = slot;
            this.parameterTypes = paramTypes;
            this.exceptionTypes = exceptTypes;      // may be null
            this.returnType = returnType;
        }
    

    于是利用在 native 从 ClassObject 获得方法的核心参数,然后利用 JNI 构造出一个 Java 层的 Method 对象,传递到 Java 层。
    以 slot 为例,

    /*
     * Convert a method pointer to a slot number.
     *
     * We use positive values starting from 0 for virtual methods, negative
     * values starting from -1 for static methods.
     */
    static int methodToSlot(const Method* meth){
      ClassObject* clazz = meth>clazz;
      int slot;
      
      if(dvmIsDriectMethod(meth)){
        slot = meth - clazz->directMethods;
        slot = -(slot+1);
      }else {
        slot = meth - clazz->virtualMethods;
      }
      return slot;
    }
    

    知道了 Method 中的参数来由,接下来就要看 invokeNative 的正身了。

    static void Dalvik_java_lang_reflect_Method_invokeNative(const u4* args,
        JValue* pResult)
    {
        // ignore thisPtr in args[0]
        Object* methObj = (Object*) args[1];        // null for static methods
        ArrayObject* argList = (ArrayObject*) args[2];
        ClassObject* declaringClass = (ClassObject*) args[3];
        ArrayObject* params = (ArrayObject*) args[4];
        ClassObject* returnType = (ClassObject*) args[5];
        int slot = args[6];
        bool noAccessCheck = (args[7] != 0);
        const Method* meth;
        Object* result;
    
        /*
         * "If the underlying method is static, the class that declared the
         * method is initialized if it has not already been initialized."
         */
        meth = dvmSlotToMethod(declaringClass, slot);
        assert(meth != NULL);
    
        if (dvmIsStaticMethod(meth)) {
            if (!dvmIsClassInitialized(declaringClass)) {
                if (!dvmInitClass(declaringClass))
                    goto init_failed;
            }
        } else {
            /* looks like interfaces need this too? */
            if (dvmIsInterfaceClass(declaringClass) &&
                !dvmIsClassInitialized(declaringClass))
            {
                if (!dvmInitClass(declaringClass))
                    goto init_failed;
            }
    
            /* make sure the object is an instance of the expected class */
            if (!dvmVerifyObjectInClass(methObj, declaringClass)) {
                assert(dvmCheckException(dvmThreadSelf()));
                RETURN_VOID();
            }
    
            /* do the virtual table lookup for the method */
            meth = dvmGetVirtualizedMethod(methObj->clazz, meth);
            if (meth == NULL) {
                assert(dvmCheckException(dvmThreadSelf()));
                RETURN_VOID();
            }
        }
    
        /*
         * If the method has a return value, "result" will be an object or
         * a boxed primitive.
         */
        result = dvmInvokeMethod(methObj, meth, argList, params, returnType,
                    noAccessCheck);
    
        RETURN_PTR(result);de
    
    init_failed:
        /*
         * If initialization failed, an exception will be raised.
         */
        LOGD("Method.invoke() on bad class %s failed",
            declaringClass->descriptor);
        assert(dvmCheckException(dvmThreadSelf()));
        RETURN_VOID();
    }
    

    没什么说的,就是将由 JNI 传来的参数转为 native 层的对象,然后利用 slot 寻找到 method,随后调用dvmInvokeMethod( )@Stack.cpp,然后根据是否是 native 方法,选择直接执行还是利用对应的解释器执行。


    那么现在来看 Field 的set( ) 和 get( );
    同理,我们先看createFieldObject( ) @Reflect.cpp

    static Object* createFieldObject(Field* field, const ClassObject* clazz){
      Object* result = NULL;
      Object* fieldObj = NULL;
      StringObject* nameObj = NULL;
      ClassObject* type;
      char* mangle;
      char* cp;
      int slot;
    
      fieldObj = dvmAllocObject(gDvm.classJavaLangReflectField, ALLOC_DEFAULT);
      
      cp = mangle = strdup(field->signature);
      type = convertSignaturePartToClass(&cp, clazz);
      
      nameObj = dvmCreateStringFromCstr(field->name);
      
      slot = fieldToSlot(field, clazz);
    
     JValue unused;
     dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectField_init, fieldObj, &unused, clazz, type, nameObj, slot);
    
      result  fieldObj;
    }
    

    从 Method 的创建可以想到,Field 的创建也是相同的形式。当然继续跟踪也证明了这件事情。


    接下里看 Field 的 set

    //Field.java
    public void set(Object object, Object value) throws IllegalAccessException, IllegalArgumentException{
      setField(object, declaringClass, type, slot, flag, value);
    
    private native void setField(Object p, Class<?> declaringClass, Class<?> type, int slot, boolean noAccessCheck, Object value) throws IllegalAccessException;
    }
    
    //java_lang_reflect_Field.cpp
    
    static void Dalvik_java_lang_reflect_Field_setField(const u4 args, JValue* pResult){
      Object* obj = (Object*) args[1];
      ClassObject* declaringClass = (ClassObject*) agrs[2];
      ClassObject* fieldType = (ClassObject*) args[3];
      int slot = args[4];
      boolean noAccessCheck = (args[5] != 0);
      Object* valueObj = (Object*) args[6];  
      Field* field;
      JValue value;
    
      field = validateFieldAccess(obj, declaringClass, slot, true, noAccessCheck);
    
      setFieldValue(field, obj, &value);
      
      RETURN_VOID();
    }
    
    static void setFieldValue(Field* field, Object* obj, const JValue* value){
      if(dvmIsStaticField(field){
        //可以看到,如果是 static field,那么 obj 传神马都是无关紧要的,因为会被忽略
        return setStaticFieldValue((StaticField*) field, value);
      }else{
        return setInstFieldValue((InstField*) field, obj, value);
      }
    }  
    
    static void setInstFieldValue(InstField* ifield, Object* obj, const JValue* value){
      // 可以看到,field 是否是 volatile 也会影响不同
      if(!dvmIsVolatileField(ifield)){
        switch(ifield->signature[0]){
          case 'Z':
            dvmSetFieldBoolean(obj, ifield->byteOffset, value->z);
            break;    
          ...
          case 'L':
          case '[':
            dvmSetFieldObject(obj, ifield->byteOffset, (Object*) value->l);
            break;
        }
      }else{
     switch(ifield->signature[0]){
          case 'Z':
            dvmSetFieldBooleanVilatile(obj, ifield->byteOffset, value->z);
            break;    
          ...
          case 'L':
          case '[':
            dvmSetFieldObjectVolatile(obj, ifield->byteOffset, (Object*) value->l);
            break;
        }
      }
    }
    

    我们只看非 volatile 的 field 的保存

    //ObjectInlines.h
    INLINE void dvmSetFieldObject(Object* obj, int offset, Object* val){
      JValue& lhs = (JValue*)BYTE_OFFSET(obj, offset);
      lhs->l = val;
      dvmWriteBarrierField(obj, &lhs->l);
    }
    
    //WriteBarrier.h
    INLINE void dvmWriteBarrierField(const Object *obj, void *addr){
      dvmMarkCard(obj);
    }
    

    现在谈谈动态代理。稍微了解的一点的人都知道动态代理是虚拟机为你创建了 Proxy的内部类,并且实现了你传入的接口,同时使用了传入的 classLoader;后面越研究越发现动态代理的诡异。还是特地开一篇好了。

    相关文章

      网友评论

          本文标题:反射支撑

          本文链接:https://www.haomeiwen.com/subject/vochfctx.html