MyBatis3源码深度解析(十)MyBatis常用工具类(三)MetaObjectMetaClass

文章目录

    • 3.4 MetaObject
    • 3.5 MetaClass

3.4 MetaObject

MetaObject是MyBatis提供的反射工具类,可以方便地获取和设置对象的属性值。

该工具类在MyBatis源码中出现的概率非常高。

假设有两个实体类:用户信息User和订单信息Order,一个用户可以有多笔订单,因此User类中通过一个List对象记录用户的订单信息。

public class User {private List<Order> orderList;private Integer id;private String name;// constructor getter setter ...
}public class Order {private String orderNo;private String goodsName;// constructor getter setter ...
}

下面通过一个示例研究使用MetaObject工具类获取User对象的属性信息:

@Test
public void testMetaObject() {List<Order> orderList = new ArrayList<Order>() {{add(new Order("order01", "紫金红葫芦"));add(new Order("order02", "羊脂玉净瓶"));}};User user = new User();user.setName("银角大王");user.setOrderList(orderList);MetaObject metaObject = SystemMetaObject.forObject(user);// 获取第一笔订单的名称System.out.println(metaObject.getValue("orderList[0].goodsName"));// 获取第二笔订单的编号System.out.println(metaObject.getValue("orderList[1].orderNo"));// 设置第二笔订单的编号metaObject.setValue("orderList[1].orderNo", "order999");// 获取第二笔订单的编号System.out.println(metaObject.getValue("orderList[1].orderNo"));// 判断User对象中是否有orderNo属性System.out.println("是否有orderNo属性及其getter方法:" + metaObject.hasGetter("orderNo"));// 判断User对象中是否有name属性System.out.println("是否有name属性及其getter方法:" + metaObject.hasGetter("name"));
}

控制台打印执行结果:

紫金红葫芦
order02
order999
是否有orderNo属性及其getter方法:false
是否有name属性及其getter方法:true

由示例可知,MetaObject工具类不仅可以获取对象的属性信息,还能修改对象的属性。

示例代码的第一步,是通过SystemMetaObject类的forObject()方法获取一个MetaObject对象。

源码1org.apache.ibatis.reflection.SystemMetaObjectpublic static MetaObject forObject(Object object) {return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY,new DefaultReflectorFactory());
}

由 源码1 可知,SystemMetaObject的forObject()方法会转调MetaObject的forObject()方法。

源码2org.apache.ibatis.reflection.MetaObjectpublic static MetaObject forObject(Object object, ObjectFactory objectFactory,ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {if (object == null) {return SystemMetaObject.NULL_META_OBJECT;}return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
}private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory,ReflectorFactory reflectorFactory) {this.originalObject = object;this.objectFactory = objectFactory;this.objectWrapperFactory = objectWrapperFactory;this.reflectorFactory = reflectorFactory;// object的类型是User,因此会进入else结构中if (object instanceof ObjectWrapper) {this.objectWrapper = (ObjectWrapper) object;} else if (objectWrapperFactory.hasWrapperFor(object)) {this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);} else if (object instanceof Map) {this.objectWrapper = new MapWrapper(this, (Map) object);} else if (object instanceof Collection) {this.objectWrapper = new CollectionWrapper(this, (Collection) object);} else {this.objectWrapper = new BeanWrapper(this, object);}
}

由 源码2 可知,MetaObject的forObject()方法会调用MetaObject的构造方法,创建一个MetaObject实例。在构造方法中,由于参数object的类型是User,因此会进入else结构中,创建一个BeanWrapper。

下面研究一下metaObject.getValue("orderList[0].goodsName")的执行过程。

源码3:org.apache.ibatis.reflection.MetaObjectpublic Object getValue(String name) {PropertyTokenizer prop = new PropertyTokenizer(name);if (!prop.hasNext()) {return objectWrapper.get(prop);}MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return null;}return metaValue.getValue(prop.getChildren());
}

由 源码3 可知,getValue()方法的首先会构造一个PropertyTokenizer对象。

源码4org.apache.ibatis.reflection.property.PropertyTokenizerpublic 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变量中,剩余的保存到children变量中name = fullname.substring(0, delim);children = fullname.substring(delim + 1);} else {// 单层属性,只保存name变量,children变量为空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);}}@Overridepublic boolean hasNext() {// 判断children变量是否为空return children != null;}
}

由 源码4 可知,PropertyTokenizer的构造方法会对属性名进行分析。

在示例代码中,传入的fullName的值为"orderList[0].goodsName",包含小数点,所以会进入第一个if分支,最终得到name=“orderList[0]”,children=“goodsName”,indexedName=“orderList[0]”。

又因为name的值包含"[",所以也会进入第二个if分支,最终index=“0”,name=“orderList”。

回到getValue()方法继续执行,因为hasNext()方法是判断children变量是否为空,显然该方法会返回true,因此!prop.hasNext()为false,程序不会进入if分支,而是直接执行下面的metaObjectForProperty()方法,传入参数indexedName=“orderList[0]”。

源码5org.apache.ibatis.reflection.MetaObjectpublic MetaObject metaObjectForProperty(String name) {// 第二次执行getValue方法Object value = getValue(name);return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}

由 源码5 可知,metaObjectForProperty()方法会第二次调用getValue()方法,但传入的参数值是"orderList[0]"

它会再次创建一个PropertyTokenizer对象,只是该对象持有的参数与之前的不同,这次的对象中children变量为null。

由于children变量为null,则!prop.hasNext()为true,getValue()方法会进入if分支,执行ObjectWrapper对象的get()方法。由前面的分析可知,创建的ObjectWrapper对象的类型是BeanWrapper。

源码6org.apache.ibatis.reflection.wrapper.BeanWrapper@Override
public Object get(PropertyTokenizer prop) {// 判断索引值是否为空if (prop.getIndex() != null) {// 不为空,根据Collection的方式处理Object collection = resolveCollection(prop, object);return getCollectionValue(prop, collection);}// 为空,按照Bean的方式处理return getBeanProperty(prop, object);
}

由 源码6 可知,BeanWrapper的get()方法首先判断索引值是否为空,如果不为空,说明是集合,则根据Collection的方式处理;如果为空,则按照Bean的方式处理。

显然,示例代码有索引值index=0,会进if分支,根据Collection的方式处理,调用resolveCollection方法:

源码7org.apache.ibatis.reflection.wrapper.BaseWrapperprotected Object resolveCollection(PropertyTokenizer prop, Object object) {// prop.getName() → orderListif ("".equals(prop.getName())) {return object;}return metaObject.getValue(prop.getName());
}

由 源码7 可知,如果prop.getName()不为空,则会第三次调用MetaObject的getValue()方法。

在示例代码中,此时prop.getName()的值是"orderList",因此不会进入if分支,而是第三次调用MetaObject的getValue()方法。

它会第三次创建一个PropertyTokenizer对象,只是该对象持有的参数与之前的都不同,这次的对象中index和children变量均为null。

因此,跟第二次一样,会转调BeanWrapper的get()方法。只是这一次prop.getIndex()为null,按照Bean的方式处理,调用getBeanProperty()方法。

源码8org.apache.ibatis.reflection.wrapper.BeanWrapperprivate Object getBeanProperty(PropertyTokenizer prop, Object object) {try {// 利用反射机制获取属性的getter方法Invoker method = metaClass.getGetInvoker(prop.getName());try {// 执行getter方法获取属性值return method.invoke(object, NO_ARGUMENTS);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}} // catch ......
}

由 源码8 可知,getBeanProperty()方法会根据属性名利用反射机制获取该属性的getter方法,再执行getter方法获取属性值

即 源码7 中resolveCollection()方法的返回值,是一个orderList集合:

回到 源码7 ,继续执行getCollectionValue()方法:

源码9org.apache.ibatis.reflection.wrapper.BaseWrapperprotected Object getCollectionValue(PropertyTokenizer prop, Object collection) {if (collection instanceof Map) {return ((Map) collection).get(prop.getIndex());}// 获取索引值int i = Integer.parseInt(prop.getIndex());if (collection instanceof List) {// 强转为List集合,并取得对应索引的值return ((List) collection).get(i);} else if (collection instanceof Object[]) {return ((Object[]) collection)[i];} // else if ... 其他类型的处理
}

由 源码9 可知,getCollectionValue()方法会判断collection参数的类型,并强转为指定的类型,同时还会根据索引值进行取值。示例代码中,collection参数是一个List集合,因此会强转为一个List集合,同时索引值是"0",因此会返回orderList集合的索引为0的元素。

因此,第三次调用MetaObject的getValue()方法的返回值就是这个索引为0的元素,同时也是第二次调用getValue()方法的返回值,也就是 源码5 的metaObjectForProperty()方法的第一步的返回值。

由 源码5 可知,metaObjectForProperty()方法的第二步是调用MetaObject的forObject()方法创建一个MetaObject对象,并将上一步查询得到Order对象作为参数传入。

程序继续执行回到了 源码3 ,即第一次调用MetaObject的getValue()方法。

接下来进入该方法的最后一行,直接嵌套调用getValue()方法(第四次),传入的prop.getChildren()参数值为"goodsName"。

第四次调用getValue()方法,首先创建一个PropertyTokenizer对象,该对象属性如下:

通过前面分析可知,第四次调用getValue()方法,最终会调用getBeanProperty()方法(源码8),根据属性名利用反射机制获取属性的getter方法,再执行getter方法获取属性值。

示例代码中,会获取"goodsName"属性的getter方法,并调用getter方法获取属性值。

至此,整个getValue()方法执行结束,最终得到了"orderList[0].goodsName"属性的值。

简单总结,getValue()方法会判断属性名是否属于多层属性,是的话拆分开来获取,先通过反射机制获取第一层的属性值,再通过反射机制依次获取更深层的属性值。

3.5 MetaClass

MetaClass也是MyBatis提供的反射工具类。

与MetaObject不同的是,MetaObject用于获取和设置对象的属性值,而MetaClass用于获取类相关的信息。如判断类是否有默认构造方法,判断类的属性是否有对应的Getter/Setter方法。

下面是一个MetaClass的使用示例:

@Test
public void testMetaClass() {MetaClass metaClass = MetaClass.forClass(Order.class, new DefaultReflectorFactory());// 获取所有拥有getter方法的属性名String[] getterNames = metaClass.getGetterNames();System.out.println(Arrays.toString(getterNames));// 判断是否有默认构造方法System.out.println("是否有默认构造方法:" + metaClass.hasDefaultConstructor());// 判断某属性是否有对应的getter/setter方法System.out.println("orderNo属性是否有对应的getter方法:" + metaClass.hasGetter("orderNo"));System.out.println("goodsName属性是否有对应的setter方法:" + metaClass.hasSetter("goodsName"));// 获取属性的类型System.out.println("goodsName属性的类型:" + metaClass.getGetterType("goodsName"));// 获取属性的值Invoker invoker = metaClass.getGetInvoker("orderNo");try {Object orderNo = invoker.invoke(new Order("NO.1986", "《西游记》"), null);System.out.println("orderNo属性的值:" + orderNo);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}
}

控制台打印执行结果:

[orderNo, goodsName]
是否有默认构造方法:true
orderNo属性是否有对应的getter方法:true
goodsName属性是否有对应的setter方法:true
goodsName属性的类型:class java.lang.String
orderNo属性的值:NO.1986

MetaClass工具类的源码,和MetaObject类似。

本节完,更多内容请查阅分类专栏:MyBatis3源码深度解析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/741384.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HCIA-Datacom题库(自己整理分类的)_50_路由判断【11道题】

1.以下两条配置命令可以实现路由器RTA去往同一目的地10.1.1.0的路由主备备份。√ [RTA]ip route-static 10.1.1.0 24 12.1.1.1 Permanent [RTA]ip route-static 10.1.1.0 24 13.1.1.1 2.动态路由协议能自动适应网络拓扑的变化。√ 3.如图&#xff0c;只需要在AR1上配置静态…

嵌入式中要能看懂这50个电路分享

大家好&#xff0c;今天主要给大家分享一下&#xff0c;嵌入式工程师常见的硬件电路。 单片机 电源 声音模块 收音机 485 蓝牙 光耦 can 光敏电阻 单片机 矩阵 单片机电路 时钟 ADC 接口电路 红外发射 显示模块 红外接收 蜂鸣器驱动 流水灯 usb供电 烧录电路 数码管 EEPROM LCD…

lnmp环境部署-im

安装nginx 配置nginx源 vim /etc/yum.repos.d/nginx.repo [nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/$releasever/$basearch/ gpgcheck1 enabled1 gpgkeyhttps://nginx.org/keys/nginx_signing.key module_hotfixestrue安装nginx yum …

宽带波束形成

上一篇介绍了窄带波束形成&#xff0c;当信号的带宽增加&#xff0c;窄带波束形成的性能会降低很多。 首先从窄带beamforming说起 阵列模式&#xff1a;M个阵元组成的线性阵列 当有个输入信号&#xff0c;&#xff0c;对应的信号的入射方向为&#xff0c;。第一个信号是感兴…

break和continue区别及使用

break和continue是跳转控制语句&#xff0c;本篇文章将详细说明这两个的概念及作用。 1、continue 这张图是2024年3月的日期图&#xff0c;列出了每日和每周。 假设上面是上班工作日期&#xff0c;一个月有三十天&#xff0c;但是每周只有七天。简单思考一下&#xff0c;每周的…

el-select下拉框无法显示 elementplus踩坑日常

在使用el-select的时候参考了官方文档&#xff0c;但下拉框无法显示 解决办法1&#xff1a;检查是否没有按需引入eloption只引入了elselect 解决办法2&#xff1a;在el-select里面加入:popper-append-to-body"false" <el-select:popper-append-to-body"fa…

Mybatis八股

Mybatis是什么 Mybatis是一个半ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了JDBC&#xff0c;加载驱动、创建连接、创建statement等繁杂的过程&#xff0c;开发者开发时只需要关注如何编写SQL语句&#xff0c;可以严格控制sql执行性能&#xff0c;灵…

运行springboot项目提示:java: 错误: 不支持发行版本 18、java: 错误: 无效的源发行版:18

java: 错误: 不支持发行版本 18 解决方法&#xff1a;修改字节码版本&#xff0c;可以多试几次。 java: 错误: 无效的源发行版&#xff1a;18 解决方法&#xff1a; 出现这些错误原因&#xff1a; spring版本与jdk版本不对应 我的spring boot版本是3.2.2&#xff0c;对应的j…

实体店新模式:线下正规实体门店的转型与升级策略

在当今竞争激烈的市场环境下&#xff0c;线下正规实体门店需要不断转型与升级&#xff0c;以适应消费者的需求和市场的变化。 作为一名开鲜奶吧5年的创业者&#xff0c;我见证了无数实体店的成功与失败&#xff0c;下面的文章我将通过具体案例&#xff0c;为大家分享一些实用的…

YOLOv9如何训练自己的数据集(NEU-DET为案列)

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文内容&#xff1a;教会你用自己数据集训练YOLOv9模型 YOLOv9魔术师专栏 ☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️ ☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️☁️ 包含注意力机制魔…

MyBatis拦截器四种类型和自定义拦截器的使用流程

文章目录 MyBatis拦截器四种类型和自定义拦截器的使用流程一、MyBatis拦截器四种类型的详细解释&#xff1a;1. **ParameterHandler 拦截器**&#xff1a;2. **ResultSetHandler 拦截器**&#xff1a;3. **StatementHandler 拦截器**&#xff1a;4. **Interceptor Chain 拦截器…

题目 2610: 第十二届省赛真题-杨辉三角形

题目描述: 下面的图形是著名的杨辉三角形&#xff1a; 如果我们按从上到下、从左到右的顺序把所有数排成一列&#xff0c;可以得到如下 数列&#xff1a; 1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1, ... 给定一个正整数 N&#xff0c;请你输出数列中第一次出现 N 是在第几…

前端实现 查询包含分页 以及封装table表格 上手即用!

表格组件是 element plus 中的table 又经过了一层封装 封装的table代码在最底下 <div class"box2"><el-radio-group v-model"radio" style"margin-bottom: 16px"><el-radio-button label"1">类型1</el-radio…

论文学习——基于枢轴点预测和多样性策略混合的动态多目标优化

论文题目&#xff1a;A dynamic multi-objective optimization based on a hybrid of pivot points prediction and diversity strategies 基于枢轴点预测和多样性策略混合的动态多目标优化&#xff08;Jinhua Zheng a,b,d, Fei Zhou a,b,∗, Juan Zou a,b, Shengxiang Yang a…

java集合框架——Collection集合概述

前言 之前学过ArrayList&#xff0c;现在接触到更多集合了。整理下笔记&#xff0c;打好基础&#xff0c;daydayup! 集合体系结构 集合分为单列结合和双列结合&#xff0c;Collection代表单列集合&#xff0c;每个元素只包含一个值。Map代表双列集合&#xff0c;每个元素包含两…

HCIP —— BGP 的社团属性

目录 BGP 的社团属性 1.0X00000000 --- internet 2.0XFFFFFF02 --- no - advertise 3.0XFFFFFF01 --- no - export 4.0XFFFFFF03 --- no-export-subconfed 配置&#xff1a; 第一步&#xff1a;使用路由策略执行对流量打上社团属性 第二步&#xff1a;在对等体通告路由之…

智慧城市与绿色出行:共同迈向低碳未来

随着城市化进程的加速&#xff0c;交通拥堵、空气污染、能源消耗等问题日益凸显&#xff0c;智慧城市与绿色出行成为了解决这些问题的关键途径。智慧城市利用信息技术手段&#xff0c;实现城市各领域的智能化管理和服务&#xff0c;而绿色出行则强调低碳、环保的出行方式&#…

1.Python是什么?——《跟老吕学Python编程》

1.Python是什么&#xff1f;——《跟老吕学Python编程》 Python是一种什么样的语言&#xff1f;Python的优点Python的缺点 Python发展历史Python的起源Python版本发展史 Python的价值学Python可以做什么职业&#xff1f;Python可以做什么应用&#xff1f; Python是一种什么样的…

第十五届蓝桥杯(Web 应用开发)模拟赛 3 期-大学组(被题目描述坑惨了)

目录 1.创意广告牌 2.原子化css 3.神秘咒语 4.朋友圈 5.美食蛋白揭秘 6.营业状态变更 7.小说阅读器 8.冰岛人 9.这是一个”浏览器“ 10.趣味加密解密 总结 1.创意广告牌 这个题目不多说了&#xff0c;只要知道这些css应该都能写出来&#xff0c;不会的平时多查查文…

C++第三弹---C++入门(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 C入门 1、内联函数 1.1、概念 1.2、特性 2、auto关键字(C11) 2.1、类型别名思考 2.2、auto简介 2.3、auto的使用细则 2.3、auto不能推导的场景 …