Mybatis 源码学习-反射工具(ObjectWrapper & MetaObject)

Posted 凉茶方便面

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Mybatis 源码学习-反射工具(ObjectWrapper & MetaObject)相关的知识,希望对你有一定的参考价值。

历史文章:
Mybatis 源码学习(6)-反射工具(MetaClass)


MetaClass 可以通过反射来解析类级别类型信息,而 ObjectWrapper 是对对象的包装,可以通过字符串操作和查询对象的属性。ObjectWrapper 类继承关系如下,它提供了 BaseWrapper、BeanWrapper、MapWrapper 和 CollectionWrapper。BaseWrapper 提供集合类型解析的抽象方法,BeanWrapper 提供普通 Bean 的 get、set 方法,MapWrapper 提供 Map 类型的 put 和 get 方法,CollectionWrapper 提供集合的 add 和 addAll 方法。

ObjectWrapper

ObjectWrapper 的接口定义如下:

public interface ObjectWrapper 
  // 获取指定的属性
  // 如果封装的是普通的 Bean  对象,则调用对应的 get 方法
  // 如果封装的是集合对象,则返回对应下标的 value
  // 如果封装的是 Map 对象,则返回 key 对应的 value
  Object get(PropertyTokenizer prop);

  // 设置指定的属性值
  // 如果封装的是普通的 Bean  对象,则调用对应的 set 方法
  // 如果封装的是集合对象,则设置对应下标的 value
  // 如果封装的是 Map 对象,则设置 key 对应的 value
  void set(PropertyTokenizer prop, Object value);

  // 查找属性表达式指定的属性
  // useCamelCaseMapping 表示是否忽略表达式中的下划线
  String findProperty(String name, boolean useCamelCaseMapping);

  // 获取可读属性的名称集合
  String[] getGetterNames();

  // 获取可写属性的名称集合
  String[] getSetterNames();

  // 解析属性表达式指定的属性对应的 set 方法的参数类型
  Class<?> getSetterType(String name);
  // 解析属性表达式指定的属性对应的 get 方法的返回值
  Class<?> getGetterType(String name);

  // 判断属性表达式指定属性是否有 set/get 方法 
  boolean hasSetter(String name);
  boolean hasGetter(String name);

  // 为属性表达式指定的属性创建相应的 MetaObject 对象
  MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
  
  // 判断封装对象是否是集合
  boolean isCollection();
  
  // 向集合中添加元素,调用 Collection.add 方法
  void add(Object element);
  // 向集合中添加一批元素,调用 Collection.addAll 方法
  <E> void addAll(List<E> element);

ObjectWrapperFactory 负责创建 ObjectWrapper,系统默认提供了 DefaultObjectWrapperFactory,但是该默认工厂类不提供任何实现,仅抛出异常。在实际使用时,可以通过配置 mybatis-config.xml 在配置中设置自定义的 ObjectWrapperFactory。

BaseWrapper

BaseWrapper 是 ObjectWrapper 的子类(抽象类),它封装了 MetaObject,并且提供三个集合类型的操作方法供子类使用:resolveCollection、getCollectionValue、setCollectionValue。

BaseWrapper.resolveCollection 方法会调用 MetaObject.getValue 解析属性表达式对应的属性(不过它一般被用来解析集合类型的属性,它能兼容普通 Bean)。
BaseWrapper.getCollectionValue 和 BaseWrapper.setCollectionValue 方法类似,它们能够解析 Map、List、数组,这三种集合类型的对象,并能够根据下标操作对应的集合中的元素。

protected Object getCollectionValue(PropertyTokenizer prop, Object collection) 
  if (collection instanceof Map)  // 集合是 Map 类型,index 是 key
    return ((Map) collection).get(prop.getIndex());
   else 
    int i = Integer.parseInt(prop.getIndex()); // 其他集合类型,index 是数值下标
    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(“…”);
    
  

BeanWrapper

BeanWrapper 继承了 BaseWrapper,它封装了实际的 JavaBean 对象、该对象的 MetaClass、该对象的 MetaObject。除了类级别上的 findProperty、getGetterNames、getSetterNames、getSetterType、getGetterType、hasSetter、hasGetter方法外,BeanWrapper 还包含三个对象级别的操作方法:get、set、instantiatePropertyValue。

BeanWrapper.get、BeanWrapper.set 方法可以用来根据属性表达式获取和设置相应的属性值。

// 获取属性表达式对应的值
public Object get(PropertyTokenizer prop) 
  // 如果存在下标信息,则表示该表达式是集合类型
  if (prop.getIndex() != null) 
    // 通过 MetaObject.getValue 方法获取 object 对象中的指定集合属性;
    // 实际上 MetaObject.getValue 又重新调用了该方法,
    // 由于重新调用时不存在下标,则会直接调用 getBeanProperty 去解析属性
    Object collection = resolveCollection(prop, object);
    // 按照下标获取元素
    return getCollectionValue(prop, collection);
   else 
    // 不存在索引,直接获取 Bean 属性
    // 直接调用属性的 Invoker.invoke,即可能存在 get 方法或者 Field 本身
    return getBeanProperty(prop, object);
  


private Object getBeanProperty(PropertyTokenizer prop, Object object) 
  try 
    // 根据属性名称,查找 Reflector.getMethods 集合中相应的 GetFieldinvoker 或 Methodinvoker
    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(“…”);
  

BeanWrapper.instantiatePropertyValue 可以创建属性表达式指定的属性实例(只能是该对象直接引用的属性),同时创建该实例对应的 MetaObject。

public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) 
  MetaObject metaValue;
  Class<?> type = getSetterType(prop.getName());  // 获取 getter 的返回值类型,即属性的类型
  try 
    Object newObject = objectFactory.create(type); // 构造一个属性的实例
    // 创建该实例的 MetaObject 以便后续通过 MetaObject 操作对象
    metaValue = MetaObject.forObject(newObject, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory(), metaObject.getReflectorFactory());
    // 将属性实例设置至对象中
    set(prop, newObject);
   catch (Exception e) 
    throw new ReflectionException(“…”);
  
  return metaValue;

MapWrapper

MapWrapper 是对 Map<String, Object> 的封装,它提供的 get、set 方法都是针对 key 做操作的。

CollectionWrapper

CollectionWrapper 是对 Collection 的封装,它不提供 get、set 方法,但是提供针对集合的 add 和 addAll 方法用于添加元素。

MetaObject

MetaObject 是对 ObjectWrapper 的封装,它提供实际对象的 getValue/setValue 方法,它的内部实现均依赖 ObjectWrapper。MetaObject 包含以下字段:

private final Object originalObject; // 原始 JavaBean 对象
private final ObjectWrapper objectWrapper; // 原始 JavaBean 对象对应的 ObjectWrapper
private final ObjectFactory objectFactory; // 负责实例化 originalObject 的 工厂对象
private final ObjectWrapperFactory objectWrapperFactory; // 负责创建ObjectWrapper的工厂对象
private final ReflectorFactory reflectorFactory; // 用于创建并缓存 Reflector 对象的工厂对象 

MetaObject 的初始化需要传入原始对象、对象工厂 ObjectFactory 等,并在构造器内完成 ObjectWrapper 的初始化。

// private 修饰,避免外部创建对象
private 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)  // 如果原始对象已经是 ObjectWrapper 对象,则直接使用
    this.objectWrapper = (ObjectWrapper) object;
   else if (objectWrapperFactory.hasWrapperFor(object)) 
    // 若 ObjectWrapperFactory 能够为该原始对象创建对应的 ObjectWrapper 对象,则优先使用 ObjectWrapperFactory
    // 默认的 DefaultObjectWrapperFactory.hasWrapperFor 返回值是 false 因此不能被使用
    this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
   else if (object instanceof Map) 
    // 处理 Map 类型
    this.objectWrapper = new MapWrapper(this, (Map) object);
   else if (object instanceof Collection) 
    // 处理 Collection 类型
    this.objectWrapper = new CollectionWrapper(this, (Collection) object);
   else 
    // 如果是普通 JavaBean,则创建 BeanWrapper
    this.objectWrapper = new BeanWrapper(this, object);
  


// 静态工厂方法
public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) 
  if (object == null) 
    // 若 object 为 null ,则返回 SystemMetaObject .NULL_META_OBJECT 作为 null 对象
    return SystemMetaObject.NULL_META_OBJECT;
   else 
    return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
  
 

MetaObject 和 ObjectWrapper 中关于类级别的方法,例如 hasGetter、hasSetter、findProperty 等方法,都是直接调用 MetaClass 的对应方法实现的。而 MetaObject 关于对象的方法,例如 getValue、setValue 都是调用 ObjectWrapper 的方法实现的。

public Object getValue(String name) 
  // 解析属性表达式
  PropertyTokenizer prop = new PropertyTokenizer(name);
  if (prop.hasNext())  // 处理子表达式
    // 根据解析后的表达式创建 MetaObject
    MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
    if (metaValue == SystemMetaObject.NULL_META_OBJECT) 
      return null;
     else 
      return metaValue.getValue(prop.getChildren()); // 递归处理子表达式
    
   else 
    return objectWrapper.get(prop); // 通过 ObjectWrapper 获取指定的属性(递归出口)
  


public MetaObject metaObjectForProperty(String name) 
  Object value = getValue(name); // 获取指定的属性
  // 创建属性对应的 MetaObject
  return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);

如果存在以下类结构:

public class A 
    List<B> lists;


public class B 
    private String name;

metaObject.getValue("lists[0].name”) 为例,其中 metaObject 是对象 A 的一个实例对应的 MetaObject 对象:

  1. 调用 metaObject.getValue 将属性占位符解析为name = lists,indexName= lists[0],children = name
  2. 调用 metaObjectForProperty 处理表达式 lists[0],通过间接调用 metaObject.getValue -> objectWrapper.get,会实际调用 BeanWrapper.get 方法,解析集合类型对象,之后触发 resolveCollection 和 getCollectionValue 解析集合中的元素;
  3. 得到对象 B 后,继续解析表达式 name,即调用表示 B 对象的 metaValue.getValue(“name”) 的属性值。

MetaObject.setValue 的逻辑和 MetaObject.getValue 的逻辑一致,但是如果需要设置的值不为空,会通过 BeanWrapper.instantiatePropertyValue 创建路径上的空对象(但是如果使用下标指定集合对象,则无法创建对应下标指向的对象)。BeanWrapper.instantiatePropertyValue 内部使用 DefaultObjectFactory.create 方法通过反射创建对象:

public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) 
  MetaObject metaValue;
  // 获取属性 getter 方法的返回值类型
  Class<?> type = getSetterType(prop.getName());
  try 
     // 通过反射创建属性对象
    Object newObject = objectFactory.create(type);
    // 创建对象对应的 MetaObject
    metaValue = MetaObject.forObject(newObject, metaObject.getObjectFactory(), metaObject.getObjectWrapperFactory(), metaObject.getReflectorFactory());
    // 将创建后的对象设置到对应的属性或集合中
    set(prop, newObject);
   catch (Exception e) 
    throw new ReflectionException(“…”);
  
  return metaValue;

总结

ObjectWrapper 和 MetaObject 都是对对象的封装,ObjectWrapper 是一系列类,它提供对 JavaBean、Map、Collection 实例的操作封装,能够很方便的操作这些类型的属性、元素。MetaObject 是对 ObjectWrapper 的封装,它可以实现对任意类型的对象,通过属性表达式直接操作,而无需明确实例化对象。


参考文档:《Mybatis 技术内幕》

本文的基本脉络参考自《Mybatis 技术内幕》,编写文章的原因是希望能够系统地学习 Mybatis 的源码,但是如果仅阅读源码或者仅从官方文档很难去系统地学习,因此希望参考现成的文档,按照文章的脉络逐步学习。


欢迎关注我的公众号:我的搬砖日记,我会定时分享自己的学习历程。

以上是关于Mybatis 源码学习-反射工具(ObjectWrapper & MetaObject)的主要内容,如果未能解决你的问题,请参考以下文章

Mybatis 源码学习-反射工具(Reflector)

Mybatis 源码学习-反射工具(MetaClass)

Mybatis 源码学习-反射工具(TypeParameterResolver)

Mybatis 源码学习-反射工具(ObjectWrapper & MetaObject)

Mybatis 源码学习-反射工具(ObjectWrapper & MetaObject)

Mybatis 源码学习-类型转换(TypeHandler)