Mybatis源码学习第四课---基础支持层反射模块分析

   MyBatis 在进行参数处理、结果映射时等操作时,会涉及大量的反射操作。为了简化这些反射相关操作,MyBatisorg.apache.ibatis.reflection 包下提供了专门的反射模块,对反射操作做了近一步封装,提供了更为简洁的 API

一、JavaBean规范

JavaBean 具有如下特征:

  1. 所有的属性都是私有的(通过 getter 和 setter 访问)
  2. 拥有公有的无参构造函数
  3. 提供 setter/getter
  4. 实现 Serializable 接口

二、Reflector 和 ReflectorFactory

   2.1 Reflector 

         出于性能方面的考虑,Mybatis 不是等到使用的时候去解析 XML/反射类,而是为每一个类提供了反射器类 Reflector ,该类中存储了反射需要使用的类的元信息

        MyBatis 提供 Reflector 类来缓存类的字段名和 getter/setter 方法的元信息,使得反射时有更好的性能。使用方式是将原始类对象传入其构造方法,生成 Reflector 对象。

     Reflector 是 MyBatis 中反射模块的基础,每个 Reflector 对象都对应一个类,在 Reflector 中 缓存了反射操作需要使用的类的元信息。 Reflector 中各个字段的含义如下:

  private final Class<?> type;//对于class的类型
  //可读属性的名称集合,存在get方法即可读
  private final String[] readablePropertyNames;
  //可写属性的名称集合,存在set方法即可写
  private final String[] writablePropertyNames;
  //记录了属性相应的 setter 方法, key 是属性名称, value 是 Invoker 对象,它是对 setter 方法对应
  private final Map<String, Invoker> setMethods = new HashMap<>();
  //属性相应的 getter 方法集合, key 是属性名称, value 也是 Invoker 对象
  private final Map<String, Invoker> getMethods = new HashMap<>();
  //记录了属性相应的 setter 方法的参数值类型, key 是属性名称, value 是 setter 方法的参数类型
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  //记录 了属性相应的 getter 方法的返回位类型, key 是属性名称, value 是 getter 方法的返回位类型
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  //默认构造方法
  private Constructor<?> defaultConstructor;
  //记录所有属性的名称集合
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

在 Reflector 的构造方法中会解析指定的 Class 对象,并填充上述集合,具体实现如下:

 public Reflector(Class<?> clazz) {
    type = clazz;//初始化type对象
    //查找 clazz 的默认构造方法(元参构造方法),具体实现是通过反射遥历所有构造方法,代码并不复杂
    addDefaultConstructor(clazz);
    //处理clazz中的get方法信息,填充getMethods、getTypes
    addGetMethods(clazz);
    //处理clazz中的set方法信息,填充setMethods、setTypes
    addSetMethods(clazz);
    //处理没有get、set方法的属性
    addFields(clazz);
    //根据get、set方法初始化可读属性集合和可写属性集合
    readablePropertyNames = getMethods.keySet().toArray(new String[0]);
    writablePropertyNames = setMethods.keySet().toArray(new String[0]);
    //初始化 caseinsensitivePropertyMap 集合,其中记录了所有大写格式的属性名称
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

   

从上面类的属性中,我们可以看出:

一个反射器 Reflector 对应着一个 Class 对象
记录了默认构造函数
其余的是属性及其 setter/gettter 相关

对于一个属性(只有有 setter/getter 才能被称之为属性)
(1)如果是可读的(有 getter 方法),则 Reflector 会将其及其方法处理后放入对应的可读相关的集合中;
(2)如果是可写的(有 setter 方法),则 Reflector 会将其及其方法处理后放入对应的可写相关的集合中;
(3)最后使用 Map<String, String> caseInsensitivePropertyMap 记录所有的属性。
 

    addGetMethods 和 addSetMethods 分别获取类的所有方法,从符合 getter/setter 规范的方法中解析出字段名,并记录方法的参数类型、返回值类型等信息:

  private void addGetMethods(Class<?> clazz) {
    //字段名-get方法
    Map<String, List<Method>> conflictingGetters = new HashMap<>();
    //获取类的所有方法,及其实现接口的方法,并根据方法签名去重
    Method[] methods = getClassMethods(clazz);
    //取get 方法 条件是入参为空,且已get开头
    Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
      .forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
    resolveGetterConflicts(conflictingGetters);
  }

  public static boolean isGetter(String name) {
    return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
  }
 private void addSetMethods(Class<?> clazz) {
    Map<String, List<Method>> conflictingSetters = new HashMap<>();
    Method[] methods = getClassMethods(clazz);
    Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName()))
      .forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m));
    resolveSetterConflicts(conflictingSetters);
  }

     对 getter/setter 方法进行去重是通过类似 java.lang.String#getSignature:java.lang.reflect.Method 的方法签名来实现的,如果子类在实现过程中,参数、返回值使用了不同的类型(使用原类型的子类),则会导致方法签名不一致,同一字段就会对应不同的 getter/setter 方法,因此需要进行去重

  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;
        }
        // 字段对应了多个get方法
        Class<?> winnerType = winner.getReturnType();
        Class<?> candidateType = candidate.getReturnType();
        if (candidateType.equals(winnerType)) {
          // 返回值类型相同
          if (!boolean.class.equals(candidateType)) {
            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方法可能有多个,如getIsSave和isSave,优先取is开头的
            winner = candidate;
          }
        } else if (candidateType.isAssignableFrom(winnerType)) {
          // OK getter type is descendant
          // 可能会出现接口中的方法返回值是List,子类实现方法返回值是ArrayList,使用子类返回值方法
        } 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.");
        }
      }
      // 记录字段名对应的get方法对象和返回值类型
      addGetMethod(propName, winner);
    }
  }

    去重的方式是使用更规范的方法以及使用子类的方法。在确认字段名对应的唯一 getter/setter 方法后,记录方法名对应的方法、参数、返回值等信息。MethodInvoker 可用于调用 Method 类的 invoke 方法来执行 getter/setter 方法(addSetMethods 记录映射关系的方式与 addGetMethods 大致相同)。

private void 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值