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

Posted 凉茶方便面

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis 源码学习-反射工具(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 的源码,但是如果仅阅读源码或者仅从官方文档很难去系统地学习,因此希望参考现成的文档,按照文章的脉络逐步学习。


欢迎关注我的公众号:我的搬砖日记,我会定时分享自己的学习历程。

以上是关于Mybatis 源码学习-反射工具(Property 工具)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

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

Mybatis 源码学习-反射工具(ObjectWrapper & MetaObject)

Mybatis 源码学习-反射工具(ObjectWrapper & MetaObject)

Mybatis 源码学习-类型转换(TypeHandler)