美文网首页MyBatis源码剖析
[MyBatis源码分析 - 反射器模块 - 组件五] Prop

[MyBatis源码分析 - 反射器模块 - 组件五] Prop

作者: 小胡_鸭 | 来源:发表于2020-10-24 12:48 被阅读0次

    一、简介

      在 org.apache.ibatis.reflection 包中,有个 property 的目录,都是关于属性操作的工具类,分别是 PropertyCopierPropertyNamerPropertyTokenizer,PropertyCopier 是属性拷贝的工具类,PropertyNamer 是将根据方法名获取属性名的工具类,PropertyTokenizer 是定位属性表达式中属性的工具类。

    二、PropertyCopier

    1、功能

      将一个JavaBean对象的所有属性值,复制给另一个相同类型的bean。

    2、源码与注解

    public final class PropertyCopier {
    
      private PropertyCopier() {
        // Prevent Instantiation of Static Class
      }
    
      /**
       *
       * @param type             JavaBean对象类型
       * @param sourceBean       属性拷贝的源对象
       * @param destinationBean  属性拷贝的目标对象
       */
      public static void copyBeanProperties(Class<?> type, Object sourceBean, Object destinationBean) {
        Class<?> parent = type;
        // 当上溯还有父类时,继续循环处理找到父类中可能定义的属性
        while (parent != null) {
          // 获取类中定义的所有属性
          final Field[] fields = parent.getDeclaredFields();
          // 遍历属性并通过反射赋值
          for(Field field : fields) {
            try {
              field.setAccessible(true);
              field.set(destinationBean, field.get(sourceBean));
            } catch (Exception e) {
              // Nothing useful to do, will only fail on final fields, which will be ignored.
            }
          }
          // 类拥有的属性可能从基类中继承而来,所以上溯父类,继续循环
          parent = parent.getSuperclass();
        }
      }
    }
    

      该类是一个工具类,对外暴露 #copyBeanProperties() 的静态方法,为了防止调用代码实例化该类的对象,将构造方法声明为私有。

    三、PropertyNamer

    1、功能

      围绕方法名判断是否 getter/setter 方法,提取属性名。

    2、源码与注解

    public final class PropertyNamer {
    
      private PropertyNamer() {
        // Prevent Instantiation of Static Class
      }
    
      // 根据getter/setter方法名提取对应的属性名
      public static String methodToProperty(String name) {
        // 如果方法名形如isXxx,则先提取出Xxx
        if (name.startsWith("is")) {
          name = name.substring(2);
        // 如果方法名形如getYyy、setZzz,则先提出出Yyy、Zzz
        } 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'.");
        }
        // 将上面拿到的Xxx、Yyy、Zzz转化为xxx、yyy、zzz,注意只处理首字母转化为小写
        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;
      }
    
      // 根据方法名判断是否为一个getter/setter方法
      public static boolean isProperty(String name) {
        return name.startsWith("get") || name.startsWith("set") || name.startsWith("is");
      }
    
      // 根据方法名判断是否为一个getter方法,一般其方法名为getXxx、isYyy (如果属性类型为布尔型)
      public static boolean isGetter(String name) {
        return name.startsWith("get") || name.startsWith("is");
      }
    
      // 根据方法名判断是否为一个setter方法,一般其方法名为setZzz
      public static boolean isSetter(String name) {
        return name.startsWith("set");
      }
    }
    

    三、PropertyTokenizer

    1、功能

      解析属性表达式,比如结果映射或者执行SQL传参,<resultMap> 的 property 属性或 sql 元素中的参数变量占位 #{} 中的内容都可能是一个属性表达式,要正确得设置返回结果对象或者从传入的参数中找到对应的属性值替换占位,都需要先正确地解析属性表达式。

      比如一个商品交易订单中可能包含多件商品,一对多关系,当根据商品订单号查到多件商品goods,但是结果映射转化为对象时,只需要某件商品的某个属性信息,比如第一件商品的价格,则用类似 goods[0].price 这样的属性表达式来表达,当然假如商品的可以有更复杂的属性比如交易信息 tradeInfo,交易时间的属性表达式就为 goods[0].tradeInfo.trxTime,所以 PropertyTokenizer 解析的就是这种带 .[] 的表达式。

    2、成员属性

      private String name;          // 当前表达式的名称
      private String indexedName;   // 当前表达式的索引名
      private String index;         // 索引下标
      private String children;      // 子表达式
    
    • name:表达式最顶层的属性名。
    • indexedName:第一个 . 前的表达式内容。
    • index:索引下标。
    • children. 后面的子表达式。

      比如表达式 goods[0].price,indexedName 为 goods[0],name 为 goods,index 为 0,children 为 price

    3、源码与注解

    public class PropertyTokenizer implements Iterable<PropertyTokenizer>, Iterator<PropertyTokenizer> {
      private String name;          // 当前表达式的名称
      private String indexedName;   // 当前表达式的索引名
      private String index;         // 索引下标
      private 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;
        delim = name.indexOf('[');
        if (delim > -1) {
          index = name.substring(delim + 1, name.length() - 1);
          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;
      }
    
      // 实现接口定义的方法,使对象可迭代
      @Override
      public boolean hasNext() {
        return children != null;
      }
    
      @Override
      public PropertyTokenizer next() {
        return new PropertyTokenizer(children);
      }
    
      @Override
      public void remove() {
        throw new UnsupportedOperationException("Remove is not supported, as it has no meaning in the context of properties.");
      }
    
      @Override
      public Iterator<PropertyTokenizer> iterator() {
        return this;
      }
    }
    

    相关文章

      网友评论

        本文标题:[MyBatis源码分析 - 反射器模块 - 组件五] Prop

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