2.4.3.实际缓存类反射数据的 Reflector 对象

优质
小牛编辑
118浏览
2023-12-01

Reflector是mybatis中定义的一个用于描述类定义信息的对象,它缓存了指定对象的类型,可读/可写属性,getter/setter方法,以及构造器等信息,并提供了操作这些属性或方法的入口,有效的简化针对指定对象的方法和属性的反射操作。

Reflector对象的定义和实现涉及到的东西比较多,我们先简单了解一下这个类的属性定义:

public class Reflector {
    /**
     * 被封装的java对象的类型
     */
    private final Class<?> type;
    /**
     * {@link #type}的可读属性名称集合
     */
    private final String[] readablePropertyNames;
    /**
     * {@link #type}的可写属性名称集合
     */
    private final String[] writeablePropertyNames;
    /**
     * {@link #type}的set方法和执行器的映射列表
     */
    private final Map<String, Invoker> setMethods = new HashMap<>();
    /**
     * {@link #type}get方法集合和执行器的映射列表
     */
    private final Map<String, Invoker> getMethods = new HashMap<>();
    /**
     * set字段类型
     * 字段名称=>字段类型
     */
    private final Map<String, Class<?>> setTypes = new HashMap<>();
    /**
     * get字段类型
     * 字段名称=>字段类型
     */
    private final Map<String, Class<?>> getTypes = new HashMap<>();
    /**
     * {@link #type}的默认构造函数
     */
    private Constructor<?> defaultConstructor;
    /**
     * {@link #type}的大小写不敏感的属性集合,全大写属性名->属性名
     */
    private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
    ...
  }

这些属性的作用已经被简单注释出来,有关他们的赋值操作和更详细的说明会在解析Reflector对象的构造方法时给出。

Reflector对象的构造过程相对比较复杂,在构造时做了很多额外的操作,我们先整体看一下他的构造实现,之后再深入到内部依赖的各个子方法中了解详细的实现过程:

public Reflector(Class<?> clazz) {
    // 被封装的JAVA类型
    type = clazz;
    // 配置默认构造方法
    addDefaultConstructor(clazz);
    // 配置以get/is开头的方法集合
    addGetMethods(clazz);
    // 配置set方法
    addSetMethods(clazz);
    // 添加字段集合
    addFields(clazz);

    // 配置可读字段名称集合
    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
    // 配置可写字段名称集合
    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);

    // 转换为大写,并放入caseInsensitivePropertyMap中,便于后期查找
    for (String propName : readablePropertyNames) {
        caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writeablePropertyNames) {
        caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
}

简单的看过构造方法的代码之后,我们大概也能猜出来内部方法的实现,整个实现无外乎依赖于反射机制,获取到传进来的java类的属性,方法,构造器这些东西,然后处理处理,把他们保存起来,便于之后的使用。

这个东西很好理解,就是操作有点繁琐。

整个构造过程,大体上可以分为两个阶段:

  • 第一个阶段是解析Class<?> clazz对象,获取Class<?> clazz类中的基本信息(属性和方法)。

    // 被封装的JAVA类型
    type = clazz;
    // 配置默认构造方法
    addDefaultConstructor(clazz);
    // 配置以get/is开头的方法集合
    addGetMethods(clazz);
    // 配置set方法
    addSetMethods(clazz);
    // 添加字段集合
    addFields(clazz);
    
  • 第二个阶段是第一阶段的补充,使用第一阶段获取到的数据完善Reflector对象中的其他属性。

    
    // 配置可读字段名称集合
    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
    // 配置可写字段名称集合
    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
    
    // 转换为大写,并放入caseInsensitivePropertyMap中,便于后期查找
    for (String propName : readablePropertyNames) {
        caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writeablePropertyNames) {
        caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    

在第一个阶段中,Reflector会按照顺序依次处理传入的clazz对象的构造方法,getter/setter方法,以及属性。

  • 1.处理构造方法

    获取clazz的无参构造方法赋给defaultConstructor属性。

  • 2.处理getter/setter方法

    按照Java Beangetter/setter方法的定义,筛选出其中有效的方法,并反向解析出这些方法对应的属性名称,之后:

    • 将方法包装成Invoker接口的实例对象,保存到getMethods/setMethods集合中
    • 移除方法返回对象的泛型,转换成对应的实际类型,保存到getTypes/setTypes集合中。
  • 3.处理属性

    对属性的处理操作,是对上一步处理结果的补充,因为在clazz中有些属性可能没有定义getter/setter方法,所以在上一步中可能就会遗漏掉这部分属性,在这一步针对这些属性:

    • Reflector会基于反射为他们生成用于执行getter/setter操作的Invoker接口的实例对象,保存到getMethods/setMethods集合中。

      Invoker接口的作用是统一基于反射处理方法/属性的调用方式,具体的作用在后面会给出。

    • 移除属性的泛型,转换成对应的实际类型,保存到getTypes/setTypes集合中。

在第二个阶段中,Reflector会将getMethods/setMethods中所有可读/可写属性的名称保存到readablePropertyNames/writeablePropertyNames集合中, 之后,将这些可读/可写的属性名称全部大写保存到caseInsensitivePropertyMap集合中,便于后续的使用。

等这两个阶段完成了,Reflector对象的构造过程也就完成了。

大概了解了构造方法做的事情之后,我们来看一下具体的实现:

首先,Reflector肯定是保存一下传进来的类,把他作为一个实例变量,便于后续使用:

// 被缓存的JAVA类型
type = clazz;

接下来是第一阶段的第一步,获取该类的默认构造方法赋值给defaultConstructor属性:

// 配置默认构造方法
addDefaultConstructor(clazz);

默认构造方法的筛选机制比较简单,获取无参构造方法即可。

private void addDefaultConstructor(Class<?> clazz) {
       // 获取所有的构造方法
       Constructor<?>[] consts = clazz.getDeclaredConstructors();
       for (Constructor<?> constructor : consts) {
           if (constructor.getParameterTypes().length == 0) {
               // 获取无参构造
               this.defaultConstructor = constructor;
           }
       }
   }

然后是第一阶段的第二步,依次处理gettersetter方法,gettersetter方法的处理操作分别由addGetMethodsaddSetMethods两个方法来完成:

// 配置以get/is开头的方法集合
addGetMethods(clazz);
// 配置set方法
addSetMethods(clazz);

其中用于处理getter方法的addGetMethods,涉及到的操作比较多,其中主要包括了:

  • 获取指定类及其父类/接口中的所有方法定义,并筛选出其中有效的getter方法
  • 根据Java Bean规范使用getter方法的方法名获取到该方法对应的属性名称
  • 根据Java Bean规范为上一步获取到的属性名称筛选唯一的getter方法实现
  • getter方法的返回类型的泛型参数定义转换为实际的类定义,比如将List<T>转换为List<String>
  • 保存方法和属性
private void addGetMethods(Class<?> cls) {
      // 用于解决方法冲突,方法的重载和重写都会导致一个属性对应多个方法
      Map<String, List<Method>> conflictingGetters = new HashMap<>();
      //  获取被处理的类的所有方法
      Method[] methods = getClassMethods(cls);
      for (Method method : methods) {
          // 忽略所有有入参的getter方法
          if (method.getParameterTypes().length > 0) {
              continue;
          }
          String name = method.getName();
          // 获取名称已get/is开头,且不是get/is的方法。
          if ((name.startsWith("get") && name.length() > 3)
                  || (name.startsWith("is") && name.length() > 2)) {
              // 将方法转换为属性名称
              name = PropertyNamer.methodToProperty(name);
              // 记录每一个属性名称对应的方法集合
              addMethodConflict(conflictingGetters, name, method);
          }
      }
      // 处理方法冲突
      resolveGetterConflicts(conflictingGetters);
}

整个getter方法的处理流程还是挺长的,考虑到阅读的连贯性,先了解一下负责获取指定类及其父类/接口中的所有方法定义的getClassMethods方法,会获取指定类的哪些方法,排序掉哪些方法。

getClassMethods方法其实是对Class.getMethods()方法的一个增强实现,他不仅会获取指定类中定义的方法,还会获取指定类实现/继承父类/接口中定义的方法。

/**
 * Class.getMethods()方法的增强实现,不仅会获取指定类的方法,还会获取指定类实现/继承的父类和接口中定义的方法。
 *
 * @param cls 指定类
 * @return 所有方法
 */
private Method[] getClassMethods(Class<?> cls) {
    // 去重后的方法集合
    Map<String, Method> uniqueMethods = new HashMap<>();
    // 当前正在处理的java类
    Class<?> currentClass = cls;
    while (currentClass != null && currentClass != Object.class) {
        // currentClass非空,且不是Object类型

        // 缓存器所有方法
        addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());

        // we also need to look for interface methods -
        // because the class may be abstract
        Class<?>[] interfaces = currentClass.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            // 处理其所有接口
            addUniqueMethods(uniqueMethods, anInterface.getMethods());
        }
        // 处理父类
        currentClass = currentClass.getSuperclass();
    }
    Collection<Method> methods = uniqueMethods.values();
    return methods.toArray(new Method[methods.size()]);
}

在该方法中,首先定义了一个Map<String,Method>类型的参数uniqueMethods,从属性名不难猜出,该属性用于存放去重后的方法集合。

他的key值是根据方法返回值类型,方法名称以及方法入参类型生成的一个String类型的签名,value则存放着对应的方法本身。

我们可以先看一下方法签名的计算方法,实现还是比较简单的:

/**
 * 为指定的方法生成签名
 *
 * @param method 方法
 * @return 方法签名
 */
private String getSignature(Method method) {
    StringBuilder sb = new StringBuilder();
    // 处理返回类型
    Class<?> returnType = method.getReturnType();
    if (returnType != null) {
        sb.append(returnType.getName()).append('#');
    }
    // 处理方法名称
    sb.append(method.getName());
    // 处理方法入参
    Class<?>[] parameters = method.getParameterTypes();
    for (int i = 0; i < parameters.length; i++) {
        if (i == 0) {
            sb.append(':');
        } else {
            sb.append(',');
        }
        sb.append(parameters[i].getName());
    }
    return sb.toString();
}

因为方法的签名是根据方法返回值、名称、入参类型来生成的,因此如果子类重写父类的方法,子类方法和父类方法的签名将会是一致的,对于这种场景,mybatis会保留子类的方法实现。

在定义了uniqueMethods属性之后,mybaits接下来会递归处理当前类及其父类和接口,取出其中声明的所有方法,计算出方法签名,并存放到uniqueMethods集合内。

!!需要注意的是,如果当前被处理的类有父类定义且父类定义不是Object,那么整个获取当前类及其父类和接口,取出其中声明的所有方法,计算出方法签名,并存放到uniqueMethods集合内这一步骤,会被递归执行,直到当前被处理的父类声明为空,或者当前被处理类的父类声明为Object

private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) {
    for (Method currentMethod : methods) {
        // 跳过桥接方法
        if (!currentMethod.isBridge()) {
            // 获取方法签名
            String signature = getSignature(currentMethod);
            // check to see if the method is already known
            // if it is known, then an extended class must have
            // overridden a method
            // 如果方法不存在,保存该方法
            if (!uniqueMethods.containsKey(signature)) {
                uniqueMethods.put(signature, currentMethod);
            }
        }
    }
}

在保存方法的过程中,mybatis会跳过对JDK自动生成的桥接方法的处理,为符合要求的方法生成一个签名(签名的计算规则前面已经讲过),之后在uniqueMethods中不存在该方法签名的前提下,保存该方法。

点解了解JDK的桥接方法

这样,我们通过getClassMethods方法就可以获取到指定类及其父类和接口中定义的所有方法,这里的所有方法指排除掉被子类重写的方法后的方法集合

了解完getClassMethods会获取一个类的哪些方法之后,我们再继续看addGetMethods方法。

addGetMethods方法中,定义了一个Map<String, List<Method>>类型的conflictingGetters属性,conflictingGetters的key值用于存放getter方法对应的属性名称,value值则存放该属性名称对应的getter方法集合。

// 用于解决方法冲突,方法的重载和重写都会导致一个属性对应多个方法
Map<String, List<Method>> conflictingGetters = new HashMap<>();

为什么会出现一个属性名称对应多个getter方法的场景呢? 这是因为在Reflector中,属性名的生成仅仅是依据方法名来完成的, 因为方法的重写和重载都会导致出现多个同名方法,进而就会出现一个属性名对应多个方法的场景, 同时依据Java Bean的规范,boolean类型的属性可能有get/is两种getter方法,也会出现一个属性名对应多个方法的场景。

点击了解JavaBean规范

在声明了conflictingGetters属性之后,mybatis调用getClassMethods方法获取到当前java对象中获取到需要被处理的方法集合之后。

// 获取所有方法,该方法会获取指定类实现/继承的父类/接口中的方法
Method[] methods = getClassMethods(cls);

之后,Reflector会依次处理集合中每个方法,因为在这里我们需要处理的是getter类型的方法,所以会忽略掉有入参的方法,同时按照Java Bean规范,只处理以get/is开头且方法名不完全是get/is的方法。

因为忽略掉了有入参的方法,所以理论上来讲,就排除掉了多个重载方法同时出现在方法集合内的场景,因此在该方法集合内,每个方法名,只会对应一个方法。

for (Method method : methods) {
   // 忽略所有有入参的getter方法
   if (method.getParameterTypes().length > 0) {
       continue;
   }
   String name = method.getName();
   // 获取名称已get/is开头,且不是get/is的方法。
   if ((name.startsWith("get") && name.length() > 3)
           || (name.startsWith("is") && name.length() > 2)) {
       // 将方法转换为属性名称
       name = PropertyNamer.methodToProperty(name);
       // 记录每一个属性名称对应的方法集合,生成用于解决方法冲突的集合
       addMethodConflict(conflictingGetters, name, method);
   }
}

具体到每个方法的处理细节上,Reflector首先借助一个叫做PropertyNamer的类将方法名称按照Java Bean规范反向解析出属性名,然后调用addMethodConflict方法,保存属性名和方法之间的映射关系,PropertyNamer这个类我们待会再说。

addMethodConflict方法的作用就是往前面定义的conflictingGetters集合中填充属性名和getter方法的映射关系。

private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
      // 如果不存在指定属性名对应的方法列表,则新建一个方法列表
      List<Method> list = conflictingMethods.computeIfAbsent(name, k -> new ArrayList<>());
      // 保存方法
      list.add(method);
  }

完成了上述操作后,Reflector就会调用resolveGetterConflicts方法来处理conflictingMethods集合,为每个属性选择一个有效的、最符合Java Bean规范的最优getter方法:

// 处理方法冲突
resolveGetterConflicts(conflictingGetters);
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
    // 期望能够从重载的方法中获取最终的候选方法
    for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
        // 保存最终采用的方法
        Method winner = null;
        // 当前属性名称
        String propName = entry.getKey();
        for (Method candidate : entry.getValue()) {
            // 处理每一个有效方法
            if (winner == null) {
                // 第一个方法赋值后会跳过处理
                winner = candidate;
                continue;
            }
            // 获取目前胜出的方法的返回类型
            Class<?> winnerType = winner.getReturnType();
            // 获取当前候选方法的返回类型
            Class<?> candidateType = candidate.getReturnType();
            if (candidateType.equals(winnerType)) {
                // 两个方法返回类型一致,但是不是boolean类型
                if (!boolean.class.equals(candidateType)) {
                    // 这里单独拆出来boolean类型,是因为在javabean规范中,他可以同时有`get*()`和`is*()`方法。
                    // 非boolean类型的属性同时有了两个getter方法,
                    // 比如name属性同时包含了`getName()`和`isName()`这两个方法。
                    throw new ReflectionException(
                            "Illegal overloaded getter method with ambiguous type for property "
                                    + propName + " in class " + winner.getDeclaringClass()
                                    + ". This breaks the JavaBeans specification and can cause unpredictable results.");
                } else if (candidate.getName().startsWith("is")) {
                    // 针对boolean类型的属性,如果同时有`get*()`和`is*()`方法优先选择is开头方法。
                    winner = candidate;
                }
            } else if (candidateType.isAssignableFrom(winnerType)) {
                // 候选方法的返回值是目前胜利方法返回值的超类,不需要处理,使用范围较小的子类。
                // OK getter type is descendant
            } else if (winnerType.isAssignableFrom(candidateType)) {
                // 候选方法的返回值是目前胜利方法返回值的子类,交换胜利身份
                winner = candidate;
            } else {
                // 重载方法的返回值类型无任何关系。
                throw new ReflectionException(
                        "Illegal overloaded getter method with ambiguous type for property "
                                + propName + " in class " + winner.getDeclaringClass()
                                + ". This breaks the JavaBeans specification and can cause unpredictable results.");
            }
        }
        // 保存getter方法
        addGetMethod(propName, winner);
    }
}

上面的方法看起来很长,实际上不算复杂,最优getter方法计算规则如下:

  • 如果属性对应的多个方法返回类型一致,表示该方法同时有getis开头的两个方法,但是除了boolean类型的属性优先使用is开头的方法,其余类型的属性都不允许有两种getter方法。

  • 如果属性对应的多个方法返回类型不一致,那么多个方法返回值之间必须存在继承关系,此时就尽可能选择返回值类型更精确的的方法作为最优方法。

在我们获取到属性名对应的最优getter方法之后,Reflector就会调用addGetMethod方法,来完成保存属性和方法的操作。

// 保存getter方法
addGetMethod(propName, winner);
private void addGetMethod(String name, Method method) {
    if (isValidPropertyName(name)) {
        // 保存到getter方法中
        getMethods.put(name, new MethodInvoker(method));
        // 获取方法的返回类型(移除泛型转换为实际类型)
        Type returnType = TypeParameterResolver.resolveReturnType(method, type);
        // 保存属性及其对应的java类型的关系
        getTypes.put(name, typeToClass(returnType));
    }
}

addGetMethod方法中只会处理合法的属性名和方法,属性名是否合法的判断操作是由isValidPropertyName方法来完成的.

/**
 * 校验是否为有效的属性名称
 *
 * @param name 属性名称
 */
private boolean isValidPropertyName(String name) {
    return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name));
}

针对属性名合法的数据,mybatis会把前面获取到的最优getter方法包装成MethodInvoker类型的Invoker实例, 保存到getMethods集合中。

// 保存到getter方法中
getMethods.put(name, new MethodInvoker(method));

通过TypeParameterResolverresolveReturnType方法将方法返回值类型的泛型转换为实际类型,之后将获取到实际类型通过typeToClass方法由Type转换为Class类型之后再放入到getTypes集合中。

// 获取方法的返回类型(移除泛型转换为实际类型)
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
// 保存属性及其对应的java类型的关系
getTypes.put(name, typeToClass(returnType));

MethodInvoker以及TypeParameterResolver的解析,在后面都会陆续给出。

我们现在只需要简单的了解一下MethodInvoker的作用是简化方法的反射操作,TypeParameterResolverresolveReturnType方法的作用是移除方法返回值的泛型定义,将其转化为实际类型就可以了。

addGetMethod方法中,我们调用了typeToClass方法:

// 保存属性及其对应的java类型的关系
getTypes.put(name, typeToClass(returnType));

typeToClass方法的作用是将Type类型转换为Class类型。

private Class<?> typeToClass(Type src) {
    Class<?> result = null;
    if (src instanceof Class) {
        // 普通对象
        result = (Class<?>) src;
    } else if (src instanceof ParameterizedType) {
        // 处理参数化泛型,获取泛型所属类型,例如:Map<K,V> 得到Map。
        result = (Class<?>) ((ParameterizedType) src).getRawType();
    } else if (src instanceof GenericArrayType) {
        // 获取泛型数组中元素的类型
        Type componentType = ((GenericArrayType) src).getGenericComponentType();
        if (componentType instanceof Class) {
            // 泛型数组中元素类型是Class
            result = Array.newInstance((Class<?>) componentType, 0).getClass();
        } else {
            // 泛型数组中元素类型还是泛型
            // 递归处理
            Class<?> componentClass = typeToClass(componentType);
            result = Array.newInstance(componentClass, 0).getClass();
        }
    }

    if (result == null) {
        // src是一个TypeVariable或者WildcardType
        result = Object.class;
    }
    return result;
}

整个解析操作的逻辑比较简单,针对Type类型的src属性,他的处理逻辑如下:

  • 如果src就是一个普通的Class,直接返回该类型即可,
  • 如果src是一个ParameterizedType(参数化泛型),获取该参数化泛型所属类型,例如:泛型K的定义是List<K>,那么就得到List
  • 如果Type是一个GenericArrayType(泛型数组),获取该数组元素的泛型定义,如果不是泛型直接返回元素类型即可,如果是泛型则递归调用typeToClass方法。
  • 如果src不满足上面任何一种条件,则返回Object.class.

到这里Reflector对于getter方法的处理就完成了。

接下来Reflector会继续处理指定的类中的setter方法,并添加相应的属性到setMethodssetTypes中。

Reflector对于setter方法的处理和getter方法的处理大致是相同的,他的实现依赖于addSetMethods方法。

// 配置set方法
addSetMethods(clazz);

addSetMethods方法中,Reflector同样定义了一个用于保存属性名和其对应方法的集合,只不过这次命名为conflictingSetters,同样调用了getClassMethods方法获取所需处理的所有方法。

// 因为重载而产生冲突的setter方法集合
Map<String, List<Method>> conflictingSetters = new HashMap<>();
// 获取需要处理的所有方法
Method[] methods = getClassMethods(cls);

之后,Reflector会依次处理集合中每个方法,因为在这里我们需要处理的是setter类型的方法,所以要求方法必须有且仅有一个入参定义,同时按照Java Bean规范,只处理以set开头且方法名不完全是set的方法。

具体到每个方法的处理细节上,Reflector依然借助PropertyNamer将方法名称按照Java Bean规范反向解析出对应的属性名,然后调用addMethodConflict方法,保存属性名和方法之间的映射关系。

在这里,addMethodConflict方法的作用是往前面定义的conflictingSetters集合中填充属性名和setter方法的映射关系。

// 因为重载而产生冲突的setter方法集合
Map<String, List<Method>> conflictingSetters = new HashMap<>();
// 获取需要处理的所有方法
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
    String name = method.getName();
    // 以set开头,且有且只有一个入参
    if (name.startsWith("set") && name.length() > 3) {
        if (method.getParameterTypes().length == 1) {
            // 获取对应的属性名称
            name = PropertyNamer.methodToProperty(name);
            // 保存方法
            addMethodConflict(conflictingSetters, name, method);
        }
    }
}
// 处理setter方法冲突
resolveSetterConflicts(conflictingSetters);

完成了上述操作后,Reflector这次会调用resolveSetterConflicts方法来处理conflictingSetters集合,为每个属性选择一个有效的、最符合Java Bean规范的最优setter方法:

resolveSetterConflicts方法和resolveGetterConflicts方法在实现上有很大的区别。

private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
     for (String propName : conflictingSetters.keySet()) {
         // 处理每个属性对应的setter方法集合

         // 获取属性对应的setter方法集合
         List<Method> setters = conflictingSetters.get(propName);
         // 尝试从getTypes中获取到属性对应的类型
         Class<?> getterType = getTypes.get(propName);
         // 最终匹配的方法
         Method match = null;
         ReflectionException exception = null;
         for (Method setter : setters) {
             // 获取入参类型
             Class<?> paramType = setter.getParameterTypes()[0];
             if (paramType.equals(getterType)) {
                 // should be the best match
                 // 方法入参类型和属性类型一致,那他就是最佳选型
                 match = setter;
                 break;
             }
             if (exception == null) {
                 try {
                     // 尝试获取两个方法中更好的setter方法
                     match = pickBetterSetter(match, setter, propName);
                 } catch (ReflectionException e) {
                     // there could still be the 'best match'
                     match = null;
                     exception = e;
                 }
             }
         }
         if (match == null) {
             throw exception;
         } else {
             // 保存setter方法
             addSetMethod(propName, match);
         }
     }
 }

resolveSetterConflicts方法中,会依次处理每个属性定义:

针对具体属性的处理操作,Reflector会先获取到属性对应的setter方法集合,并尝试从这个setter方法集合中选择一个最优的setter方法用于后续的保存操作。

在筛选最优的setter方法的过程中,Reflector会先尝试从getTypes集合中获取该属性对应的java类型(如果该属性有getter方法的话,它对应的java类型会在上一步的addGetMethods方法中被添加到getTypes集合中)。

如果得到了对应的java类型,我们会用这个java类型和每个setter方法的入参进行对比,如果二者一致,那么这个setter方法就是我们需要的最优setter方法。

如果所有的setter方法的入参都不能匹配这个java类型,Reflector就会把所有的setter方法交给pickBetterSetter方法进行两两对比,选出方法入参更精准的那个方法作为最佳setter方法。

如果在对比的过程中发现两个setter方法的入参类型之间没有任何关系,Reflector就无法选择最佳setter方法,所以就会触发异常。

/**
 * 该方法的作用是为指定的属性选择一个更好的setter方法定义。
 * setter方法的选择取决于两个setter方法的入参的关系,如果两个方法的入参类型没有关联,将会导致异常。
 * 否则则会获取两个入参类型中相对更精确地方法。
 *
 * @param setter1  第一种setter方法
 * @param setter2  第二种setter方法
 * @param property 属性名
 * @return 最佳setter方法
 */
private Method pickBetterSetter(Method setter1, Method setter2, String property) {
    if (setter1 == null) {
        return setter2;
    }
    // 分别获取两个方法的入参
    Class<?> paramType1 = setter1.getParameterTypes()[0];
    Class<?> paramType2 = setter2.getParameterTypes()[0];

    // 获取两个类中定义更精确的那个
    if (paramType1.isAssignableFrom(paramType2)) {
        return setter2;
    } else if (paramType2.isAssignableFrom(paramType1)) {
        return setter1;
    }
    throw new ReflectionException("Ambiguous setters defined for property '" + property + "' in class '"
            + setter2.getDeclaringClass() + "' with types '" + paramType1.getName() + "' and '"
            + paramType2.getName() + "'.");
}

当我们获取到了最佳setter方法之后,Reflector就会委托addSetMethod方法把属性和最佳setter方法保存到setMethodssetTypes中。

private void addSetMethod(String name, Method method) {
    if (isValidPropertyName(name)) {
        // 保存属性和setter方法执行器的关系
        setMethods.put(name, new MethodInvoker(method));
        // 获取方法实际入参类型(移除泛型转换为实际类型)
        Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
        // 保存属性及其setter方法入参类型关系
        setTypes.put(name, typeToClass(paramTypes[0]));
    }
}

addSetMethod方法的处理和addGetMethod方法类似,他同样依赖isValidPropertyName方法来筛选出需要处理的合法属性名。

针对属性名合法的数据,addSetMethod方法同样会把前面获取到的最优setter方法包装成MethodInvoker类型的Invoker实例, 然后保存到setMethods集合中。

// 保存属性和setter方法执行器的关系
setMethods.put(name, new MethodInvoker(method));

区别在于,对于泛型的处理,addSetMethod是 通过TypeParameterResolverresolveParamTypes方法将方法入参类型的泛型转换为实际类型,之后将获取到的类型通过typeToClassType转换为Class类型放入到setTypes集合中。

到这里,对于setter方法的处理也完成了,接下来就是第一阶段的第三步,处理指定类的属性定义。

Reflector对于指定类属性的处理操作是由addFields方法来完成的。

// 添加字段集合
addFields(clazz);

addFields方法其实是对addGetMethods方法和addSetMethods方法的补充,因为在clazz中有些属性可能没有定义getter/setter方法,所以在addGetMethods方法和addSetMethods方法中可能就会遗漏掉这部分属性的处理。

private void addFields(Class<?> clazz) {
    // 获取所有字段定义
    Field[] fields = clazz.getDeclaredFields();
    for (Field field : fields) {
        // 只处理没有getter/setter方法的属性
        if (!setMethods.containsKey(field.getName())) {
            // issue #379 - removed the check for final because JDK 1.5 allows
            // modification of final fields through reflection (JSR-133). (JGB)
            // pr #16 - final static can only be set by the classloader
            int modifiers = field.getModifiers();
            if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
                // 非静态常量
                addSetField(field);
            }
        }
        if (!getMethods.containsKey(field.getName())) {
            addGetField(field);
        }
    }
    if (clazz.getSuperclass() != null) {
        // 递归处理父类
        addFields(clazz.getSuperclass());
    }
}

addFields方法就是为了处理这些被遗漏掉的属性:

在该方法的具体实现上,Reflector会先获取到clazz的所有属性定义:

// 获取所有字段定义
Field[] fields = clazz.getDeclaredFields();

之后针对每个具体的属性,分别进行setter方法和getter方法的处理。

我们之前说过addFields方法是对addGetMethods方法和addSetMethods方法实现的补充,所以在处理属性对应的setter方法时,会要求该属性没有被保存到setMethods集合中 ,同时要求属性的定义不能是静态常量,因为静态常量的赋值操作只能在类加载器中完成,不能通过反射进行修改。 对于符合要求的属性,Reflector会将其交给addSetField方法来完成后续的处理。

if (!setMethods.containsKey(field.getName())) {
   // issue #379 - removed the check for final because JDK 1.5 allows
   //                // modification of final fields through reflection (JSR-133). (JGB)
   //                // pr #16 - final static can only be set by the classloader
   int modifiers = field.getModifiers();
   if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
       // 处理非静态常量
       addSetField(field);
   }
}

在处理属性的getter方法时,基于同样的原因,会要求该属性没有被保存到getMethods集合中,对于符合要求的属性,Reflector会将其交给addGetField方法来完成后续的处理。

if (!getMethods.containsKey(field.getName())) {
   addGetField(field);
}

addGetField方法和addSetMethods方法的实现类似,他们都会先使用isValidPropertyName方法校验属性名称的合法性,针对合法的属性:

  • addGetField方法会把属性包装成GetFieldInvoker类型的Invoker实例存放到getMethods集合中,
  • addSetMethods方法会把属性包装成SetFieldInvoker类型的Invoker实例存放到setMethods集合中.

之后,两个方法都会通过TypeParameterResolverresolveFieldType将属性定义中泛型转换为实际类型,并通过typeToClass方法将其由Type类型转换为Class类型之后存放到setTypes或者getTypes集合中。

private void addSetField(Field field) {
    if (isValidPropertyName(field.getName())) {
        // 配置字段的setter方法
        setMethods.put(field.getName(), new SetFieldInvoker(field));
        // 获取该字段类型
        Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
        // 保存字段名称和字段类型的关系
        setTypes.put(field.getName(), typeToClass(fieldType));
    }
}
private void addGetField(Field field) {
    if (isValidPropertyName(field.getName())) {
        // 配置字段getter方法
        getMethods.put(field.getName(), new GetFieldInvoker(field));
        // 获取该字段的类型
        Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
        // 保存字段名称和字段类型的关系
        getTypes.put(field.getName(), typeToClass(fieldType));
    }

在完成当前类中所有的属性的处理操作之后,Reflector会获取当前clazz的父类作为参数继续递归调用addFields方法完成父类中属性定义的处理操作。

当所有父类中的属性都被处理完成之后,Reflector对象构造方法的第一阶段(解析Class<?> clazz对象,获取Class<?> clazz类中的基本信息(属性和方法))就已经完成了。

接下来就是依赖前端获取到的数据,完成Reflector对象中其他属性的赋值操作。

在第二个阶段中,Reflector会获取getMethods集合中所有可读属性的名称将其保存到readablePropertyNames集合中:

// 配置可读字段名称集合
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);

获取setMethods集合中所有可写属性的名称将其保存到writeablePropertyNames集合中:

// 配置可写字段名称集合
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);

之后,将所有属性名称转换为大写,保存到caseInsensitivePropertyMap集合中:

// 转换为大写,并放入caseInsensitivePropertyMap中,便于后期查找
 for (String propName : readablePropertyNames) {
     caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
 }
 for (String propName : writeablePropertyNames) {
     caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
 }

完成了上述属性的赋值操作之后,Reflector对象的构造方法也就完成了。

在解析Reflector对象的构造方法时,我们提到过他的实现依赖于Invoker及其实现类。