9.MetaObject

news/2025/1/11 7:12:47/

1.MetaObject功能

MetaObject类相当于一个工具类,Mybatis在sql参数设置和结果集映射里经常使用到这个对象。

映射是指结果集中的列填充至JAVA Bean属性。这就必须用到反射,而Bean的属性 多种多样的有普通属性、对象、集合、Map都有可能。为了更加方便的操作Bean的属性,MyBatis提供了MeataObject 工具类,其简化了对象属性的操作。其具体功能如下:

  • 查找属性:勿略大小写,支持驼峰、支持子属性 如:blog.comment.user_name(需要开启useCamelCaseMapping)

  • 获取属性

    1. 基于点获取子属性 user.name
    2. 基于索引获取列表值 users[1].id
    3. 基于key获取map值 user[name]
  • 设置属性

    1. 可设置子属性值 authorPO.username

    2. 支持自动创建子属性(必须带有空参构造方法,且不能是集合)

      如Blog类有AuthorPO类型的字段时。

      metaObject.setValue(“authorPO.username”, “张三”);

      会自动创建AuthorPO对象,并设置username属性值为"张三"

2.基本使用方法

以一篇博客为例,简单介绍MetaObject的使用方法

image-20210917222253817

    MetaObject metaObject = new Configuration().newMetaObject(new BlogPO());// 直接操作对象属性metaObject.setValue("title", "南京新闻");BlogPO blogPO = (BlogPO) metaObject.getOriginalObject();System.out.println(metaObject.getValue("title"));System.out.println("--------------------------------");// 基于点获取子属性authorPO.username// 自动创建对象AuthorPOmetaObject.setValue("authorPO.username", "张三");System.out.println(metaObject.getValue("authorPO.username"));System.out.println("--------------------------------");// 基于索引获取列表值// metaObject.setValue("commentPOList[0].comment", "写的不错,点赞"); List与array不会自动创建ArrayList<Object> commentPOList = new ArrayList<>();commentPOList.add(new CommentPO());commentPOList.add(new CommentPO());metaObject.setValue("commentPOList", commentPOList);metaObject.setValue("commentPOList[0].comment", "写的不错,点赞");System.out.println(metaObject.getValue("commentPOList[0].comment"));System.out.println("--------------------------------");// 基于key获取map值// blogHashMap[blog]与blogHashMap.blog一样metaObject.setValue("blogHashMap", new HashMap<>());metaObject.setValue("blogHashMap[blog]", "一篇博客");System.out.println(metaObject.getValue("blogHashMap.blog"));System.out.println("--------------------------------");metaObject.setValue("authorPO.authorHashMap", new HashMap<>());metaObject.setValue("authorPO.authorHashMap[author]", "作者");System.out.println(metaObject.getValue("authorPO.authorHashMap[author]"));System.out.println("--------------------------------");// 勿略大小写,支持驼峰String blob_web = metaObject.findProperty("blob_web", true);System.out.println(blob_web);metaObject.setValue(blob_web, "blob_web");System.out.println(metaObject.getValue(blob_web));

3.MetaObject内部结构

public class MetaObject {// 原始对象private final Object originalObject;// 对原始对象的一个包装  private final ObjectWrapper objectWrapper;private final ObjectFactory objectFactory;private final ObjectWrapperFactory objectWrapperFactory;private final ReflectorFactory reflectorFactory;// 新建MetaObjectprivate MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {this.originalObject = object;this.objectFactory = objectFactory;this.objectWrapperFactory = objectWrapperFactory;this.reflectorFactory = reflectorFactory;if (object instanceof ObjectWrapper) {this.objectWrapper = (ObjectWrapper) object;} else if (objectWrapperFactory.hasWrapperFor(object)) {this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);} else if (object instanceof Map) {// 原始对象为Map,使用MapWrapper进行包装this.objectWrapper = new MapWrapper(this, (Map) object);} else if (object instanceof Collection) {// 原始对象为Collection,使用CollectionWrapper包装this.objectWrapper = new CollectionWrapper(this, (Collection) object);} else {// 原始对象为JAVABean,使用BeanWrapper包装this.objectWrapper = new BeanWrapper(this, object);}}// 创建MetaObject  public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {// 被包装的原始对象为NULL时,新建一个NULL_META_OBJECT(其originalObject 为NullObject,mybatis自己定义的)  if (object == null) {return SystemMetaObject.NULL_META_OBJECT;} else {return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);}}// ...  
}

为了实现上述功能,MetaObject 相继依赖了ObjectWrapperMetaClassReflector。这四个对象关系如下:

image-20210917230124586

  • BeanWrapper: 功能与MeataObject类似,不同点是BeanWrapper只针对单个当前对象属性进行操作,不能操作子属性。
  • MetaClass :类的反射功能支持,获能获取整完整类的属性,包括属性的属性。
  • Reflector :类的反射功能支持,仅支持当前类的属性。

4.PropertyTokenizer

PropertyTokenizer 是mybatis的属性分词器,提供了对象和集合的一种概念形式, 记录了属性有没有子属性

// commentPOList[0].comment为例
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {// commentPOListprivate String name;// commentPOList[0]private final String indexedName;// 0private String index;// commentprivate 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;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;}
}

image-20210917232042673

index为list所在的索引,也可以为Map的key

5.MetaObject#getValue流程及源码解析

5.1流程

image-20210917232737880

MeataObject.getValue()

获取值的入口,首先根据属性名"comments[0].user.name" 解析成PropertyTokenizer,并基于属性中的“.” 来判断是否有子属性值,如果有就先调用getValue()获取当前属性对象。并把当前属性对象转换为元对象,然后在递归调用getValue()获取子属性下的属性。直到最后的name属性获得。

MeataObject.setValue()

流程与getValue()类似,不同在于如果子属性不存在,则会尝试创建子属性。

5.2源码解析

5.2.1MetaObject#getValue

public Object getValue(String name) {// 属性分词器  PropertyTokenizer prop = new PropertyTokenizer(name);// 含有子属性,递归  if (prop.hasNext()) {// 获取当前属性的值  MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());if (metaValue == SystemMetaObject.NULL_META_OBJECT) {return null;} else {// 这里递归调用,直到最后一层的子属性return metaValue.getValue(prop.getChildren());}} else {// 无子属性,直接获取属性值 return objectWrapper.get(prop);}
}

5.2.2MetaObject#metaObjectForProperty

public MetaObject metaObjectForProperty(String name) {// 获取当前属性的值,这个值没有子属性 Object value = getValue(name);// 把当前属性值,封装为元对象return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
}

5.2.3ObjectWrapper#get

ObjectWrapper为原始对象的包装对象,有可能为MapWrapper和BeanWrapper

image-20210917234554816

BeanWrapper

public Object get(PropertyTokenizer prop) {// 当前属性是集合List,Map,Array[]if (prop.getIndex() != null) {Object collection = resolveCollection(prop, object);return getCollectionValue(prop, collection);} else {return getBeanProperty(prop, object);}
}

MapWrapper

// 适用于blogHashMap.blog的解析
// blogHashMap为Map,blog为Key
public Object get(PropertyTokenizer prop) {if (prop.getIndex() != null) {Object collection = resolveCollection(prop, map);return getCollectionValue(prop, collection);} else {return map.get(prop.getName());}
}

5.2.4BaseWrapper#resolveCollection

protected Object resolveCollection(PropertyTokenizer prop, Object object) {if ("".equals(prop.getName())) {return object;} else {// 解析出集合的值,可以是List,Map,Array[]  return metaObject.getValue(prop.getName());}
}

5.2.5BaseWrapper#getCollectionValue

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);}// 各种数组  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.");}}
}

5.2.6BeanWrapper#getBeanProperty

private Object getBeanProperty(PropertyTokenizer prop, Object object) {try {// 获取属性的Getter方法  Invoker method = metaClass.getGetInvoker(prop.getName());try {// 反射调用,获取值  return method.invoke(object, NO_ARGUMENTS);} catch (Throwable t) {throw ExceptionUtil.unwrapThrowable(t);}} catch (RuntimeException e) {throw e;} catch (Throwable t) {throw new ReflectionException("Could not get property '" + prop.getName() + "' from " + object.getClass() + ".  Cause: " + t.toString(), t);}
}

6.Reflector

最主要的反射模块,MetaClass的操作就是依赖Reflector完成的。

JavaBean 具有如下特征:

  1. 所有的属性都是私有的(通过 getter和setter 访问)
  2. 拥有公有的无参数构造函数
  3. 提供 setter/getter
  4. 实现 Serializable 接口

MetaClass的操作都是对Reflector的成员变量进行操作的。

  • 字段的getter,setter方法,反射调用
  • 字段是否有getter,setter方法
  • 字段的类型等等
public class Reflector {/*** 对应的Class 类型,当前类*/private final Class<?> type;/*** 可读属性的名称集合,可读属性就是存在相应getter 方法的属性,初始值为空数纽*/private final String[] readablePropertyNames;/*** 可写属性的名称集合,可写属性就是存在相应setter 方法的属性,初始值为空数纽*/private final String[] writablePropertyNames;/*** 属性相应的setter 方法, key 是属性名称, value 是Invoker 对象,它是对setter 方法对应*/private final Map<String, Invoker> setMethods = new HashMap<>();/*** 属性相应的getter 方法, key 是属性名称, value 是Invoker 对象,它是对setter 方法对应*/private final Map<String, Invoker> getMethods = new HashMap<>();/*** 属性相应的setter 方法的参数值类型, ke y 是属性名称, value 是setter 方法的参数类型*/private final Map<String, Class<?>> setTypes = new HashMap<>();/*** 属性相应的getter 方法的返回位类型, key 是属性名称, value 是getter 方法的返回位类型*/private final Map<String, Class<?>> getTypes = new HashMap<>();/*** 默认构造方法,无参构造方法*/private Constructor<?> defaultConstructor;/*** 所有属性名称的集合(不分大小写),key=属性名去大写,value=类中真正的属性名,* 这里通过设置key全部大小,能够保证就算写得再烂的驼峰命名,可以拿到正确的属性。*/private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();public Reflector(Class<?> clazz) {type = clazz;addDefaultConstructor(clazz);addGetMethods(clazz);addSetMethods(clazz);addFields(clazz);readablePropertyNames = getMethods.keySet().toArray(new String[0]);writablePropertyNames = setMethods.keySet().toArray(new String[0]);for (String propName : readablePropertyNames) {caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);}for (String propName : writablePropertyNames) {caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);}}// ...  
}    

6.1defaultConstructor

// 查找clazz的无参构造方法,通过反射遍历所有构造方法,找到构造参数集合长度为0的。
private void addDefaultConstructor(Class<?> clazz) {Constructor<?>[] constructors = clazz.getDeclaredConstructors();Arrays.stream(constructors).filter(constructor -> constructor.getParameterTypes().length == 0).findAny().ifPresent(constructor -> this.defaultConstructor = constructor);
}

6.2addGetMethods

private void addGetMethods(Class<?> clazz) {// 方法名称去除set,get,is后的属性Map<String, List<Method>> conflictingGetters = new HashMap<>();// 所有的方法  Method[] methods = getClassMethods(clazz);// GET方法的参数个数为0,并且以get或者is开头(排除此两个单词)Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName())).forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));resolveGetterConflicts(conflictingGetters);
}// 获取当前类以及父类中定义的所有方法的唯一签名以及相应的Method对象
private Method[] getClassMethods(Class<?> clazz) {Map<String, Method> uniqueMethods = new HashMap<>();Class<?> currentClass = clazz;while (currentClass != null && currentClass != Object.class) {// 为每个方法生成唯一签名,并记录到uniqueMethods集合中// 签名为:返回值类型#方法名:参数类型,参数类型(多个参数用,拼接)addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());Class<?>[] interfaces = currentClass.getInterfaces();for (Class<?> anInterface : interfaces) {addUniqueMethods(uniqueMethods, anInterface.getMethods());}currentClass = currentClass.getSuperclass();}Collection<Method> methods = uniqueMethods.values();return methods.toArray(new Method[0]);
}// 方法转属性字段名
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;
}

7.MetaClass

MetaClass 通过与属性工具类的结合, 实现了对复杂表达式的解析,实现了获取指定描述信息的功能。

7.1 成员变量

image-20210918002354494

MetaClass 有两个成员变量, 分别是 ReflectorFactoryReflector

7.2 创建

MetaClass 的构造函数是私有的。

  /*** MetaClass 构造函数*/private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {this.reflectorFactory = reflectorFactory;this.reflector = reflectorFactory.findForClass(type);}

但是, 其提供了两个创建的方法。 这两个方法也是通过该方法进行创建对象的。 该方法通过 Class 对象进行了 Reflector 对象的创建, 并赋值给成员变量。

  /*** 跟上面的是一样的*/public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {return new MetaClass(type, reflectorFactory);}

通过属性进行创建

  /*** 通过属性名称, 获取属性的 MetaClass*/public MetaClass metaClassForProperty(String name) {Class<?> propType = reflector.getGetterType(name);return MetaClass.forClass(propType, reflectorFactory);}

7.3 方法

该类中, 最重要的方法是:

    /*** 解析属性表达式* 会去寻找reflector中是否有对应的的属性* @param name* @param builder* @return*/private StringBuilder buildProperty(String name, StringBuilder builder) {// 解析属性表达式PropertyTokenizer prop = new PropertyTokenizer(name);// 是否有子表达式if (prop.hasNext()) {// 查找对应的属性String propertyName = reflector.findPropertyName(prop.getName());if (propertyName != null) {// 追加属性名builder.append(propertyName);builder.append(".");// 创建对应的 MetaClass 对象MetaClass metaProp = metaClassForProperty(propertyName);// 解析子表达式, 递归metaProp.buildProperty(prop.getChildren(), builder);}} else {// 根据名称查找属性String propertyName = reflector.findPropertyName(name);if (propertyName != null) {builder.append(propertyName);}}return builder;}

理解了这个方法(递归, 该类中有很多类似的), 就可以很好的对这个类进行理解, 以查找(richType.richProperty)为例:

  1. 通过 PropertyTokenizer 对表达式进行解析, 得到当前的 name=richType, children=richProperty
  2. 从 reflector 中查找该 richType 属性
  3. 将 richType 添加到 builder 中
  4. 使用 metaClassForProperty 创建 richType 的 MetaClass
  5. 递归调用自身来处理子表达式

退出的条件就是没有子表达式。 这个就是为了, 我们类中有成员变量是类, 我们可以通过其找到他们的所有类及其属性。

der);
}
} else {
// 根据名称查找属性
String propertyName = reflector.findPropertyName(name);
if (propertyName != null) {
builder.append(propertyName);
}
}
return builder;
}


理解了这个方法(递归, 该类中有很多类似的), 就可以很好的对这个类进行理解, 以查找(richType.richProperty)为例:1. 通过 PropertyTokenizer 对表达式进行解析, 得到当前的 name=richType, children=richProperty
2. 从 reflector 中查找该 richType 属性
3. 将 richType 添加到 builder 中
4. 使用 metaClassForProperty 创建 richType 的 `MetaClass`。
5. 递归调用自身来处理子表达式退出的条件就是没有子表达式。 这个就是为了, 我们类中有成员变量是类, 我们可以通过其找到他们的所有类及其属性。注意, 在此过程中, `ReflectorFactory` 一直是同一个, 而其内部缓存了多个 `Reflector` 对象。

http://www.ppmy.cn/news/228951.html

相关文章

作为ARM Cortex-M家族的继承者 Cortex-M23与M33有哪五大特色?

http://news.eeworld.com.cn/xfdz/article_2017011259937.html 集微网消息&#xff0c;ARM处理器在嵌入式设备领域的应用非常广泛。基于ARM Cortex处理器的片上系统&#xff08;SoC&#xff09;解决方案适用于多种嵌入式设计细分市场&#xff0c;如物联网、电机控制、医疗、…

【开发心得】Chrome/Edge 91版本SameSite by default cookies被移除后的解决方案

前言:场景是cas单点登录&#xff0c;使用iframe解决跨域问题。 chrome 版本大于80 且 小于91的情况&#xff0c;可以通过 在chrome浏览器地址栏输入chrome://flags并回车 在搜索栏中输入SameSite by default cookies搜索&#xff0c;并禁用如图中的两项设置 &#xff0c;改为…

MT290\291\292\295\296\298\299

MT 290 Advice of Charges,Interest and Other Adjustments收费、利息和其他调整通知 参考Common Group Message&#xff0c;MTn90Advice of Charges, Interest and Other Adjustments&#xff0c;查看此电文类型相关详情。 http://blog.csdn.net/shuytu/article/details/780…

深度学习预预训练与MMPretrain

MMPretrain算法库 优势&#xff1a; 含有各种主干网络模型自监督学习功能多模态学习功能丰富的数据集含有训练技巧和策略易用&#xff0c;例如可解释性分析、推理api 包含多种丰富任务的开箱即用推理api 图像分类图像语义描述视觉问答视觉定位检索 安装步骤 配置文件中含有…

Qcom_hexagon编译自动获取目录和特定文件的方法

一&#xff0c;简介 本文主要介绍&#xff0c;如何在高通hexagon ide中的hexagon.min中添加获取目录和.c文件的方法&#xff0c;供参考。 二&#xff0c;具体命令 OBJ_PATH : ./awinic_sp_module/algo_libINCLUDE_PATH : $(shell find $(OBJ_PATH ) -type d) SRC_C_FILE : …

ABIDE Preprocessed 结构态MRI数据集介绍及下载

ABIDE数据集介绍及下载 ABIDE Prerocessed项目是在ABIDE I 项目的基础上发展而来&#xff0c;主要是对ABIDE I中采集到的原始数据进行了一定的预处理和初步的特征提取。针对于fMRI和sMRI数据有着不同的处理方式&#xff0c;本次主要对其中提供的sMRI预处理结果进行介绍&#xf…

C++的“友元”是否会破坏类的封装?

1. 简述 C中的友元&#xff0c;即“友元类”或“友元函数”&#xff0c;历来有两种说法。有人认为它是“开后门”&#xff0c;破坏了类的封装设计&#xff0c;但也有人&#xff0c;包括C之父在内&#xff0c;他们的观点是“友元增强了类的封装”。 C之父原文 Does “friend” …

扫描二维码实现后台管理系统登录

二维码实现后台登录简单版的过程&#xff1a; 1.前端页面实时生成带有唯一标识uid的二维码&#xff0c;二维码内容为系统内的接口。&#xff08;后端也可以生成&#xff09; 2.当扫描二维码时访问后台接口&#xff0c;后台的接口获取uid&#xff0c;组装数据访问微信端接口获取…