Mybatis 源码学习(5)-反射工具(Property 工具)

历史文章:
Mybatis 源码学习(4)-反射工具(ObjectFactory)


Property 工具集包含 PropertyTokenizerPropertyNamerPropertyCopier 三个工具类,其中 PropertyTokenizer 用于符号解析,PropertyNamer 用于解析和判断属性名称,PropertyCopier 用于对象属性复制。

PropertyTokenizer

PropertyTokenizer 用于解析属性占位符表达式,如,将字符串 orders[0].items[0].name 解析为实际的对象关系,需要将属性和下标以及在属性之间做出分割。

public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
  private String name; // 当前表达式的名称
  private final String indexedName; // 当前表达式带索引的名称
  private String index; // 索引下标
  private final String children; // 子表达式

  public PropertyTokenizer(String fullname) {
    int delim = fullname.indexOf('.'); // 查找 “.” 字符的位置
    if (delim > -1) {
      name = fullname.substring(0, delim); // “.” 字符之前的被认为是属性名
      children = fullname.substring(delim + 1); // “.” 字符之后的被认为是子表达式
    } else {
      name = fullname;
      children = null;
    }
    indexedName = name; // indexedName 赋值,默认是带下标的字符串,但是如果已完全解析,就只有属性名
    delim = name.indexOf('['); // 下标的开始字符位置
    if (delim > -1) {
      index = name.substring(delim + 1, name.length() - 1); // 解析下标(从 delim 到最后,没有 trim 操作,所有要求编写时不能带空格)
      name = name.substring(0, delim); // 下标字符开始之前的所有字符认为是属性名称
    }
  }

  public String getName() {
    return name;
  }

  public String getIndex() {
    return index;
  }

  public String getIndexedName() {
    return indexedName;
  }

  public String getChildren() {
    return children;
  }

  public boolean hasNext() {
    return children != null;
  }

  public PropertyTokenizer next() {
    return new PropertyTokenizer(children);
  }

  public void remove() {
    throw new UnsupportedOperationException(“…”);
  }
}

初始化 PropertyTokenizer 时,就会自动解析第一个属性,PropertyTokenizer.next 方法会继续解析后边的属性,它会利用 . 分割符,为属性分组,通过[字符,分割属性名称和属性下标。如解析属性表达式 orderx[0].items[0].name 时,其过程如下:
在这里插入图片描述

PropertyNamer

PropertyNamer 有四个方法:methodToPropertyisPropertyisGetterisSetter,主要用于方法名到属性名的转换,还有方法类型的判断。

public final class PropertyNamer {

  // 防止实例化该对象
  private PropertyNamer() { }

  // 将方法转化为属性名称(即,即使只有set/get方法,没有对应字段也能够解析为属性)
  // 通过前缀判断
  public static String methodToProperty(String name) {
    if (name.startsWith("is")) {
      name = name.substring(2);
    } else if (name.startsWith("get") || name.startsWith("set")) {
      name = name.substring(3);
    } else {
      throw new ReflectionException("Error parsing property name '" + name + "'.  Didn't start with 'is', 'get' or 'set'.");
    }

    // 检查除去前缀后的名称,如果前缀后的第一个字符是大写,则改为小写
    // 这个方法有点儿粗暴,像是 island 这样的单词作为方法名,就会被分割为 land,会不符合预期
    if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
      name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
    }

    return name;
  }

  // 是否是属性字段(通过前缀判断)
  public static boolean isProperty(String name) {
    return name.startsWith("get") || name.startsWith("set") || name.startsWith("is");
  }

  // 判断是否是 get 方法(is 对应 boolean 方法)
  public static boolean isGetter(String name) {
    return name.startsWith("get") || name.startsWith("is");
  }

  // 是否是 set 方法
  public static boolean isSetter(String name) {
    return name.startsWith("set");
  }
}

PropertyCopier

PropertyCopier 是属性拷贝的工具类,它可以在两个相同类型的对象间拷贝内容,它只有一个方法:copyBeanProperties

public final class PropertyCopier {

  // 防止实例化该对象
  private PropertyCopier() {  }

  public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean) {
    Class<?> parent = type;
    while (parent != null) {
      // 获取所有的 field
      final Field[] fields = parent.getDeclaredFields();
      for(Field field : fields) {
        try {
          // 按照继承关系,将属性按照名称逐级从 sourceBean 设置到 destinationBean 对象中
          field.setAccessible(true);
          field.set(destinationBean, field.get(sourceBean));
        } catch (Exception e) { }
      }
      parent = parent.getSuperclass(); // 继续设置父类中的字段
    }
  }
}

总结

Property 工具主要的功能就是解析对象属性,如 PropertyTokenizer 是为了解析 xml 文件中的对象占位符,PropertyNamer 是为了处理对象中的方法到属性之间的映射和判断,PropertyCopier 是两个具有继承关系的对象之前进行属性赋值。


参考文档:《Mybatis 技术内幕》

本文的基本脉络参考自《Mybatis 技术内幕》,编写文章的原因是希望能够系统地学习 Mybatis 的源码,但是如果仅阅读源码或者仅从官方文档很难去系统地学习,因此希望参考现成的文档,按照文章的脉络逐步学习。


欢迎关注我的公众号:我的搬砖日记,我会定时分享自己的学习历程。
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值