在上一篇博客《mybatis原理分析(五)—参数处理》中介绍了参数处理的过程,分析了相关的源码。其中用到了MetaObject这个工具类来处理属性值,但并没有详细的介绍这个工具类有什么特点,又是如何工作的,所以这篇博客主要介绍MetaObject的特点和原理。
结果集映射的时候也会使用到这个工具类,所谓映射是指结果集中的列填充至JAVA Bean属性。这就必须用到反射,而Bean的属性是多种多样的有普通属性、对象、集合、Map等等。为了更加方便的操作Bean的属性,MyBatis提供了MetaObject 工具类,其简化了对象属性的操作。其具体的功能如下:
获取属性
设置属性:
为了实现上述的功能,MetaObject依赖了BeanWrapper、MetaClass、Reflector。
BeanWrapper: 功能与MeataObject类似,不同点是BeanWrapper只针对单个当前对象属性进行操作,不能操作子属性。例如可以获取当前对象的user属性,但是不能获取当前对象的user属性下的name子属性
MetaClass :类的反射功能支持,获取整完整类的属性,包括属性的属性。
Reflector :类的反射功能支持,仅支持当前类的属性。
分析这个流程可以通过编写一个测试代码,跟着debug来看
测试代码如下:
创建了一个Blog对象,里面包括了user和Comment集合,Comment集合中包括了user。
将这个Blog对象封装成MetaObject,然后调用getValue就可以获取到属性值。
首先进入的是getValue方法,第一步会对传入的参数name 进行分词
下面是具体分词的源码
在fullname中从前往后找,找到第一个“.",
接着将indexedName设置为name
然后从name中找"["
测试代码的结果如下:
第一步分词就结束了,回到getValue方法中。接着会调用hasNext判断分词结果中的children是否为空,如果不为空的话,说明有子属性。本例子中子属性不为空 成立,进入第一个if。
metaObjectForProperty是根据分词结果中的indexedName 来获得对象并封装成metaObject。
跟上面一样先分词,此时并没有子属性,所以会调用objectWrapper.get
根据分词的index来判断是否是集合,如果是集合的话调用getCollectionValue来获取属性值
如果不是的话调用getBeanProperty来获取属性值。都是通过反射来获取的。
然后代码回到了metaObjectForProperty方法
调用MetaObject.forObject 将得到的Comment[0]对象封装成MetaObject
返回到getValue方法中,
此时得到了Comments[0]对象封装后的MetaObject对象,也就是这里的metaValue。
之后会调用metaValue.getValue来解析它的属性。也就是user.name
之后的逻辑就是上面的重复的递归了,对name进行分词,分词后,根据反射得到属性对应的属性值,再将这个属性值封装成MetaObject对象。然后调用getValue继续解析子属性。直到最后没有子属性了,就这样获取到了目标属性值。
设置属性的流程和上面获取属性的流程差不多,不同在于如果子属性不存在,则会尝试创建子属性。
测试代码如下:
首先也是分词,判断有没有子属性。
如果有子属性,则先根据反射得到当前分词中indexedName的属性值,然后将它封装成MetaObject对象,如果属性值不存在,则会尝试创建,然后再用新的MetaObject递归调用setValue方法。
最终找到要设置的子属性的位置,基于反射设置属性。