MyBatis(技术NeiMu):基础支持层(Property工具集)

回顾

上一篇分析了MyBatis如何对泛型进行解析还有如何去利用ObjectFactory去创建对象

Property工具集

Property工具集主要有三个

  • PropertyTokenizer
  • PropertyNamer
  • PropertyCopier

这三个工具集是用来解析SQL映射配置文件中的ResultMap标签的表达式的

比如有如下的ResultMap

<resultMap>
	<result property = "order[0].items[0].name" column = "item1"/>
    <result property = "order[0].items[1].name" column = "item2"/>
</resultMap>

这个ResultMap的映射结果就是将item1的列的值,会变成第一个订单的第一个item的name属性,而item2会变成第一个订单的第二个item的name属性

这种由".“和”[]"组成的表达式是由PropertyTokenizer来负责解析的

PropertyTokenizer

在这里插入图片描述
可以看到,这个PropertyTokenizer是属于反射模块里面的

而且从构造方法里面就进行解析了

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) {
        //如果存在,那么就说明第一级的最后面肯定是]
        //然后取出delim索引前面的,到倒数第二个的字符串
        //该字符串就代表为索引了
      index = name.substring(delim + 1, name.length() - 1);
        //此时第一级就变成了索引前面了
      name = name.substring(0, delim);
    }
  }

可以看到,PropertyTokenizer负责的是解析order[0].item这种的字符串

  • 获取点分隔符的位置,判断有没有点分隔符
    • 如果有点分隔符就证明有两层
      • 根据点分隔符划分出了name和children,name就是order[0],children就是item
    • 如果没有分隔符,那么整个待解析的字符串就为name,children为null
  • 接下来就要解析是否有索引了,也就是【0】
    • 判断左方括号有没有,如果有就证明肯定有索引,此时直接左方括号的下一个位置到倒数第二个位置,代表的就是索引值,倒数最后一个为右方括号,此时解析出来的index就是0
    • 同时name也要进行重新划分了,name变成了开头到左方括号的前一位,也就是order

可能有人觉得那么对于order[0].item[0].name又怎么进行解析呢?

其实这就是一个简单的递归而已,划分出了children为item[0].name,此时对item进行同样的拆分解析即可

PropertyNamer

在这里插入图片描述
PropertyNamer也是位于反射模块下面的,其主要的功能就是完成方法名到属性名的转换(其实前面我们在反射模块已经看到了,如何将Get\Set方法名转换成变量名)

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'.");
    }

    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;
  }

这里就不再赘述了

PropertyCopier

在这里插入图片描述
PropertyCopier也是反射模块下的,其负责对属性进行拷贝,里面只有一个静态方法

源码如下

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 {
          try {
              //执行destinationBean的set方法,并且参数为复制对象里面的属性值
              //使用get方法获得复制对象里面的属性值,然后执行目标对象的set方法注入属性值
            field.set(destinationBean, field.get(sourceBean));
          } catch (IllegalAccessException e) {
            if (Reflector.canControlMemberAccessible()) {
              field.setAccessible(true);
              field.set(destinationBean, field.get(sourceBean));
            } else {
              throw e;
            }
          }
        } catch (Exception e) {
          // Nothing useful to do, will only fail on final fields, which will be ignored.
        }
      }
        //获取下一个父类
      parent = parent.getSuperclass();
    }
  }

可以看到,这里其实就是循环遍历了所有的父类、并且通过反射获取所有的属性,执行get方法获取到复制对象里面对应的属性值,再执行set方法注入到新生成的对象里面

MetaClass

MetaClass是用于对复杂的属性表达式进行解析的,其利用的就是前面我们学习过的Reflector和PropertyTokenizer(Reflector用于存储类的元信息,PropertyTokenizer用于解析字符串)

在这里插入图片描述
其中里面比较重要的是一个findProperty方法,该方法就是用来解析复杂的表达式的

在这里插入图片描述
可以看到,该方法主要是由buildProperty来实现的,并且可以看到,这里还会针对useCamelCaseMapping来忽略了下划线,这个useCamelCaseMapping其实就是是否开启了驼峰命名,所以大概可以估计到,MetaClass其实是根据数据库的列名来转化成属性名的

下面就来看看

buildProperty

该方法的源码如下

private StringBuilder buildProperty(String name, StringBuilder builder) {
    //对name进行解析
    PropertyTokenizer prop = new PropertyTokenizer(name);
    //判断解析过后有没有Children,也就是有没有子表达式
    //判断是否解析完全了
    if (prop.hasNext()) {
        //如果有二级,也就是还有Children子表达式
        //使用reflector去获取name对应的属性
        //name为目标类里面的一个成员属性名称
        //findPropertyName是从caseInsensitivePropertyMap集合里面去取
        //并且里面的是有get或者有set方法的属性
      String propertyName = reflector.findPropertyName(prop.getName());
      //如果可以找到对应的属性
      if (propertyName != null) {
          //builder重新组合
        builder.append(propertyName);
        builder.append(".");
          //为该属性创建对应的MetaClass对象
          //因为下面我们就要从另外一个属性的Reflector里面去解析children了
          //children是属于另外一个属性的,比如User类里面的order.item
          //现在我们从User里面解析了order,但要解析item的时候要去order里面解析
          
        MetaClass metaProp = metaClassForProperty(propertyName);
          //递归继续去进行创建!!!这里就是解析Children了
        metaProp.buildProperty(prop.getChildren(), builder);
      }
    } else {
        //如果递归到没有Children,没有子标签
        //就无序进行递归了
      String propertyName = reflector.findPropertyName(name);
      if (propertyName != null) {
        builder.append(propertyName);
      }
    }
    return builder;
  }

在这里插入图片描述
可以看到metaClassForProperty只是根据了name去从Get方式里面获取返回值类型,然后去创建对应的MetaClass!!!

简单总结一下buildProperty

  • 对传进来的name进行解析

    • 判断Children是否为空
      • 如果不为空,需j要对Children进行继续解析,使用name去创建出MetaClass,然后递归使用MetaClass再次对Children进行解析
      • 如果为空,证明已经到底了,无需再对下级进行解析了
  • 而所谓的解析,仅仅只是拼接成name.children.children1的形式而已。。。。。。

  • 而且这里要注意,使用name去创建出新的MetaClass是从Reflector的getTypes里面去获取name对应的Class类型,说白了就是该name的Get方法的返回值类型,所以如果没有Get方法这里是否报错的

hasGetter和hasSetter

hasGetter和hasSetter是用来获取属性是否有Getter方法和Setter方法的

todo

ObjectWrapper和ObjectWrapperFactory

ObjectWrapper

MetaClass是MyBatis对类级别的元信息的封装和处理,那么对于对象级别的呢?

MyBatis对于对象级别的元信息封装处理使用的是ObjectWrapper来进行处理的,ObjectWrapper接口是对对象的包装,抽象了对象的属性信息,它定义了一系列查询对象属性信息的方法,以及更新属性的方法

在这里插入图片描述
ObjectWrapper接口拥有如下的方法

  • get:调用对象对应属性的Get方法,如果对象是一个集合,则是获取该key的value

  • set:调用对象对应属性的set方法,如果对象是一个集合,则是设置对应Key的Value

  • findProperty:查找属性表达式指定的属性(是否忽略下划线来进行查找,既是否开启驼峰命名)

  • getGetterNames:查找可读属性的名称集合

  • getSetterNames:查找可写属性的名称集合

  • getSetterType:解析属性表达式指定属性的setter方法的参数类型

  • getGetterType:解析属性表达式指定属性的getter方法的返回值类型

  • hasSetter:是否有setter方法

  • hasGetter:是否有getter方法

  • isCollection:判断对象是不是集合

  • add:如果对象是集合,调用了对象的add方法

  • instantiatePropertyValue:根据属性表达式中的属性去创建相应的MetaObject对象

从该接口拥有的方法可以看到,ObjectWrapper其实就是提供了调用实例的一些方法,而且重点是对于属性的Getter和Setter方法,如果对象是一个集合,则是集合元素里面的读取和设置
在这里插入图片描述
从该ObjectWrapper里可以看出,其首先类有两个

  • CollectionWrapper:实例为集合类型的
  • BaseWrapper:实例为对象类型的,并且是一个抽象类
    • BeanWrapper:对象为JavaBean类型
    • MapWrapper:对象类型为Map接口实现类(Map不是属于Collection的!!!!)
CollectionWrapper

对于集合类型的CollectionWrapper

在这里插入图片描述
可以看到其组装了一个Collection集合,并且绝大对数的方法都是直接抛错没有实现的!!!只有add和addAll方法实现了
在这里插入图片描述
并且都是直接调用组装的Collection集合的

BaseWrapper

BaseWrapper是对于非集合类型的

在这里插入图片描述
在这里插入图片描述
可以看到,BaseWrapper是一个抽象类,并且其组装了一个MetaObject,并且没有重写ObjectWrapper里面的任何一个方法

下面就来简单介绍一下这三个方法

resolveCollection

该方法是用来解析属性表达式并获取指定的属性的

源码如下

protected Object resolveCollection(PropertyTokenizer prop, Object object) {
    //判断属性表达式的name属性是否为空
    if ("".equals(prop.getName())) {
        //如果为空
      return object;
    } else {
        //如果不为空就调用metaObject的getValue方法
        //该方法会解析name属性,获取name对应的值的
      return metaObject.getValue(prop.getName());
    }
  }
getCollectionValue

这个方法是用来获取支持当对象为数组或者Map类型时,可以根据指定索引或者Key来获取对应的值的,当然,指定索引或者Key位于表达式里面,需要进行解析

源码如下

protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
    //Map类型处理
    if (collection instanceof Map) {
      return ((Map) collection).get(prop.getIndex());
    } 
    //数组类型处理
    else {
        //获取索引
      int i = Integer.parseInt(prop.getIndex());
        //List处理
      if (collection instanceof List) {
        return ((List) collection).get(i);
      } 
        //Object数组处理
        else if (collection instanceof Object[]) {
        return ((Object[]) collection)[i];
      }
        //八大基本数组类型
        else if (collection instanceof char[]) {
        return ((char[]) collection)[i];
      } else if (collection instanceof boolean[]) {
        return ((boolean[]) collection)[i];
      } else if (collection instanceof byte[]) {
        return ((byte[]) collection)[i];
      } else if (collection instanceof double[]) {
        return ((double[]) collection)[i];
      } else if (collection instanceof float[]) {
        return ((float[]) collection)[i];
      } else if (collection instanceof int[]) {
        return ((int[]) collection)[i];
      } else if (collection instanceof long[]) {
        return ((long[]) collection)[i];
      } else if (collection instanceof short[]) {
        return ((short[]) collection)[i];
      } else {
        throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
      }
    }
  }

可以看到,对于数组的处理,其实就是把数组类型都枚举出来而已,List、Object数组和八大基本数据类型数组

setCollectionValue

有get就有set嘛,对应也会有setCollectionValue

源码如下

protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) {
    //对Map的处理
    if (collection instanceof Map) {
        //调用Put方法
      ((Map) collection).put(prop.getIndex(), value);
    } 
    //对数组的处理
    else {
        //获取索引
      int i = Integer.parseInt(prop.getIndex());
        //对List类型的处理
      if (collection instanceof List) {
        ((List) collection).set(i, value);
      } 
        //对Object数组的处理
        else if (collection instanceof Object[]) {
        ((Object[]) collection)[i] = value;
      } 
        //对八大基本数据类型数组的处理
        else if (collection instanceof char[]) {
        ((char[]) collection)[i] = (Character) value;
      } else if (collection instanceof boolean[]) {
        ((boolean[]) collection)[i] = (Boolean) value;
      } else if (collection instanceof byte[]) {
        ((byte[]) collection)[i] = (Byte) value;
      } else if (collection instanceof double[]) {
        ((double[]) collection)[i] = (Double) value;
      } else if (collection instanceof float[]) {
        ((float[]) collection)[i] = (Float) value;
      } else if (collection instanceof int[]) {
        ((int[]) collection)[i] = (Integer) value;
      } else if (collection instanceof long[]) {
        ((long[]) collection)[i] = (Long) value;
      } else if (collection instanceof short[]) {
        ((short[]) collection)[i] = (Short) value;
      } else {
        throw new ReflectionException("The '" + prop.getName() + "' property of " + collection + " is not a List or Array.");
      }
    }
  }

可以看到,set方法也是一样去枚举处理而已,这里就不再赘述了

对于下面的BeanWrapper和MapWrapper,代码也很简单,这里就不再赘述了

ObjectWrapperFactory

ObjectWrapperFactory就是负责创建ObjectWrapper的

在这里插入图片描述
里面只有两个方法

  • hasWrapperFor:判断指定的Object是否创建了ObjectWrapper
  • getWrapperFor:使用指定的MetaObject去创建ObjectWrapper

在这里插入图片描述
其实现类仅仅只有一个DefaultObjectWrapperFactory

可以看到,其hasWrapperFor方法返回了false,并且getWrapperFor方法仅仅只是抛错,并且抛错的信息还说DefaultObjectWrapperFactory不能用于提供ObjectWrapper

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值