[MyBatis源码分析 - 反射器模块 - 组件七] ObjectWrapper

一、简介

  ObjectWrapper 接口是对对象的包装,抽象了对象的属性信息,它定义了一系列查询对象属性信息、以及更新属性的方法,其类图如下:



二、接口定义

public interface ObjectWrapper {
  // 根据表达式对应的 PropertyTokenizer 对象,读写对应的属性
  // 如果封装的是集合类,则获取指定/读取key或下标的value值
  Object get(PropertyTokenizer prop);
  void set(PropertyTokenizer prop, Object value);

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

  // 获取可读/写属性集合
  String[] getGetterNames();
  String[] getSetterNames();

  // 解析属性表达式指定属性的 setter/getter 方法的参数类型
  Class<?> getSetterType(String name);
  Class<?> getGetterType(String name);

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

  // 为属性表达式指定的属性创建响应的 MetaObject 对象
  MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
  
  boolean isCollection();           // 封装的对象是否为 Collection 类型
  void add(Object element);         // 调用 Collection 中的 add() 方法
  <E> void addAll(List<E> element); // 调用 Collection 中的 addAll() 方法
}


三、BaseWrapper

  BaseWrapper 是实现 ObjectWrapper 接口的抽象子类,主要为子类 BeanWrapperMapWrapper 提供属性值的获取和设置的功能,包装了 MetaObject 对象,关于对 MetaObject 的介绍,详见:

  protected static final Object[] NO_ARGUMENTS = new Object[0];
  protected MetaObject metaObject;

  protected BaseWrapper(MetaObject metaObject) {
    this.metaObject = metaObject;
  }

  BaseWrapper 对外暴露的方法解析如下。

1、Object resolveCollection(PropertyTokenizer prop, Object object)

【功能】解析对象中的集合名,调用 MetaObject 的方法获取对应的属性值返回。
【源码与注解】

  protected Object resolveCollection(PropertyTokenizer prop, Object object) {
    // 如果表达式不合法解析不到属性名,则直接返回默认值
    if ("".equals(prop.getName())) {
      return object;
    } else {
      // 解析到属性名,调用 MetaObject 的方法获取属性值返回
      return metaObject.getValue(prop.getName());
    }
  }


2、Object getCollectionValue(PropertyTokenizer prop, Object collection)

【功能】根据属性表达式,获取对应集合中的属性值返回,这里的集合指 Map、List、Object[] 和基本类型数组。
【源码与注解】

  protected Object getCollectionValue(PropertyTokenizer prop, Object collection) {
    // 如果集合是一个 Map 对象,则表达式中的索引就代表 Map 中对应的 key,eg: map['key']
    if (collection instanceof Map) {
      return ((Map) collection).get(prop.getIndex());
    } else {
      // 如果集合是一个列表或数组,则下标肯定是一个整数,eg: list[0]/arr[0]
      int i = Integer.parseInt(prop.getIndex());
      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.");
      }
    }
  }


3、void setCollectionValue(PropertyTokenizer prop, Object collection, Object value)

【功能】设置表达式指定的集合中某个属性的值,跟上面的方法类似。
【源码】

  protected void setCollectionValue(PropertyTokenizer prop, Object collection, Object value) {
    if (collection instanceof Map) {
      ((Map) collection).put(prop.getIndex(), value);
    } else {
      int i = Integer.parseInt(prop.getIndex());
      if (collection instanceof List) {
        ((List) collection).set(i, value);
      } 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.");
      }
    }
  }


四、BeanWrapper

  BeanWrapper 继承了抽象类 BaseWrapper,因此也继承了父类中的 MetaObject 对象,并且在构造方法中根据传入参数初始化该对象,BeanWrapper 还封装了一个 JavaBean 对象及对应的 MetaClass 对象,都通过传入参数初始化和创建,源码如下:

  private Object object;        // JavaBean 对象
  private MetaClass metaClass;  // 保存 JavaBean 对象对应的类的元信息的 MetaClass 对象

  public BeanWrapper(MetaObject metaObject, Object object) {
    super(metaObject);
    this.object = object;
    this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
  }

  BeanWrapper 实现了 BaseWrapper 接口中定义的所有方法,并自定义一些方法来辅助实现,各个方法的分析如下:

1、Object get(PropertyTokenizer prop)

【功能】获取JavaBean对象中对应表达式的属性的值。

PS. 入参 prop 代表的表达式是不带子表达式的,即在调用 BeanWrapper 的上层代码中,已经完成了递归找到最末尾的子表达式的过程。

【源码与注解】

  @Override
  public Object get(PropertyTokenizer prop) {
    // (1)如果表达式带索引,证明这是个集合(Map、List、数组)
    // 调用父类方法获得对应集合的对象,再根据索引获取索引对应的值
    if (prop.getIndex() != null) {
      Object collection = resolveCollection(prop, object);
      return getCollectionValue(prop, collection);
    } else {
      // (2)表达式不带索引,调用getBeanProperty方法获取属性值
      return getBeanProperty(prop, object);
    }
  }

【解析】
(1)如果表达式形如:list[0]/map[key]/arr[0],则先获取对应属性 list/map/arr 对象的值,再获取索引对应的元素的值。
(2)如果表达式不带索引,则传入的就是个属性名,调用 #getBeanProperty() 方法,获取属性对应的值,其源码分析如下:

  private Object getBeanProperty(PropertyTokenizer prop, Object object) {
    try {
      // 得到获取属性对应的Invoker对象
      Invoker method = metaClass.getGetInvoker(prop.getName());
      try {
        // 通过Invoker封装的反射操作获取属性值
        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);
    }
  }


2、void set(PropertyTokenizer prop, Object value)

【功能】设置JavaBean对象中对应表达式的属性的值。
【源码与注解】

  @Override
  public void set(PropertyTokenizer prop, Object value) {
    if (prop.getIndex() != null) {
      Object collection = resolveCollection(prop, object);
      setCollectionValue(prop, collection, value);
    } else {
      setBeanProperty(prop, object, value);
    }
  }

【解析】
  跟 get 方法的实现类似,如果不带索引,调用 #setBeanProperty() 方法,设置对应属性的值,其源码如下:

  private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
    try {
      // 得到设置属性对应的Invoker对象
      Invoker method = metaClass.getSetInvoker(prop.getName());
      Object[] params = {value};
      try {
        // 通过Invoker封装的反射操作设置属性值
        method.invoke(object, params);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    } catch (Throwable t) {
      throw new ReflectionException("Could not set property '" + prop.getName() + "' of '" + object.getClass() + "' with value '" + value + "' Cause: " + t.toString(), t);
    }
  }


3、Class<?> getSetterType(String name)

【功能】获得表达式对应的属性的 setter 方法的参数类型。
【源码与注解】

  @Override
  public Class<?> getSetterType(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);   // 解析表达式
    // 存在子表达式
    if (prop.hasNext()) {
      // 创建 MetaObject 对象
      MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());
      // 如果 metaValue 为 SystemMetaObject.NULL_META_OBJECT,表示封装的Java对象值为NULL
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        // 通过类元信息,获取 setter 方法对应属性类型
        return metaClass.getSetterType(name);
      } else {
        // 当对象不为空时,通过对象元信息,获取 setter 方法对应属性类型,可以获得更具体的类型信息
        // 递归判断子表达式的children,然后返回
        return metaValue.getSetterType(prop.getChildren());
      }
    } else {
      // 如果不存在子表达式,直接调用MetaClass.getSetter获取属性类型
      // 这里之所以用 metaClass.getSetterType(name) 而不是  metaValue.getSetterType(name)
      // 是因为 metaValue.getSetterType 也是依赖 objectWrapper.getSetterType,如果还是调用
      // metaValue.getSetterType 会陷入无限递归,metaClass 才是递归的出口
      return metaClass.getSetterType(name);
    }
  }

【解析】
  这里有个不太好理解的地方,为什么优先使用 MetaObejct,而仅当其封装的对象为空时,才使用 MetaClass 对象?MetaClass 封装的是类的元信息,MetaObject 封装的是对象的元信息,可以将类元信息看成是对象元信息的一个子集,所以根据类原信息得到的一些类型信息可能更加具体,举个例子:

public class RichType {
    private RichType richType;
    private Map richMap = new HashMap();
    // other code
}

  RichType 中的属性 richMap,在定义时,并没有指定具体的类型,所以 key 和 value 都可以是任意的 Object 的子类,创建一个对象,并往 richMap 中压值。

 RichType object = new RichType();
 object.setRichType(new RichType());
 object.getRichType().setRichMap(new HashMap());
 object.getRichType().getRichMap().put("haha", "123");
 object.getRichType().getRichMap().put("hehe", null);

  对于 object 来说,上述代码中压入的两个值对应的表达式为 richType.richMap.haharichType.richMap.hehe,对第一个表达式来说,其值为 "123",所以其类型是 String,而第二个值或者其他没有压值的值,其值都是 null,其类型是 Object,测试代码如下:

  @Test
  public void shouldGetSetterType() {
    RichType object = new RichType();
    object.setRichType(new RichType());
    object.getRichType().setRichMap(new HashMap());
    object.getRichType().getRichMap().put("haha", "123");
    //object.getRichType().getRichMap().put("hehe", null);
    MetaObject metaObject = SystemMetaObject.forObject(object);
    Class<?> hahaCls = metaObject.getObjectWrapper().getSetterType("richType.richMap.haha");
    Class<?> heheCls = metaObject.getObjectWrapper().getSetterType("richType.richMap.hehe");
    Assert.assertEquals(String.class, hahaCls);
    Assert.assertEquals(Object.class, heheCls);
  }

  执行结果:



4、Class<?> getGetterType(String name)

【功能】获得表达式对应的属性的 getter 方法的返回值类型。
【源码】
  跟 #getSetterType 的处理过程类似。

  @Override
  public Class<?> getGetterType(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());
      if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
        return metaClass.getGetterType(name);
      } else {
        return metaValue.getGetterType(prop.getChildren());
      }
    } else {
      return metaClass.getGetterType(name);
    }
  }


5、boolean hasSetter(String name)

【功能】是否有表达式对应的属性的 setter 方法。
【源码与注解】

  @Override
  public boolean hasSetter(String name) {
    PropertyTokenizer prop = new PropertyTokenizer(name);
    if (prop.hasNext()) {
      // 如果找不到顶层属性的 setter,证明传入的表达式对应的属性不存在
      if (metaClass.hasSetter(prop.getIndexedName())) {
        MetaObject metaValue = metaObject.metaObjectForProperty(prop.getIndexedName());
        if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
          return metaClass.hasSetter(name);
        } else {
          return metaValue.hasSetter(prop.getChildren());
        }
      } else {
        return false;
      }
    } else {
      return metaClass.hasSetter(name);
    }
  }

【解析】
  处理流程跟 #getGetterType 还是大同小异,差别在于 #getGetterType 解析到不存在的属性的时候会抛出异常,异常的源头在于 Reflector.getGetterType(),如下所示:

  public Class<?> getGetterType(String propertyName) {
    Class<?> clazz = getTypes.get(propertyName);
    if (clazz == null) {
      throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'");
    }
    return clazz;
  }

  而本方法,会先判断顶层是否有 setter,如果没有子表达式的属性肯定也是不存在的,直接返回 false,实际上不做这一层判断也没事。

6、boolean hasGetter(String name)

【功能】是否有表达式对应的属性的 getter 方法。
【源码与注解】
  与 #hasSetter() 类似。

7、MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory)

【功能】为表达式指定的属性创建对应的 MetaObject 对象。
【源码与注解】

  @Override
  public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
    MetaObject metaValue;
    // 得到表达式指定属性的类型
    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("Cannot set value of property '" + name + "' because '" + name + "' is null and cannot be instantiated on instance of " + type.getName() + ". Cause:" + e.toString(), e);
    }
    return metaValue;
  }


8、其他方法

  依赖 MetaClass 的方法实现:

  • (1)#getGetterNames():调用 metaClass.getGetterNames() 实现。
  • (2)#getSetterNames():调用 metaClass.getSetterNames() 实现。
  • (3)#String findProperty(String, boolean):调用 metaClass.findProperty(String, boolean) 实现。

  不支持集合操作方法:

  @Override
  public boolean isCollection() {
    return false;
  }

  @Override
  public void add(Object element) {
    throw new UnsupportedOperationException();
  }

  @Override
  public <E> void addAll(List<E> list) {
    throw new UnsupportedOperationException();
  }


五、MapWrapper

  MapWrapper 同样是 BaseWrapper 的子类,在理解 BeanWrapper 的前提下,理解 MapWrapper 不在话下,对于 MapWrapper 的介绍,聚焦跟 BeanWrapper 的差异即可,MapWrapper 封装了一个 Map 对象,代码如下:

  private Map<String, Object> map;

  public MapWrapper(MetaObject metaObject, Map<String, Object> map) {
    super(metaObject);
    this.map = map;
  }

  获取设置属性值时,BeanWrapper 要先找到对应的 Invoker 对象,再调用其 invoke() 方法,而 MapWrapper 直接调用 Map.get/set 即可,差异如下:


  获取可读可写属性集合时,MapWrapper 会获取其键集合并转化为数组,如下:

  获取属性 getter、setter 递归处理出口时,要获取属性对象,再调用 #getClass() 获取,如果值为空则默认为 Obejct.class,源码如下:

  判断是否有表达式指定属性的 getter/setter 的实现中,setter 是固定返回 true,getter 则是根据 key 判断 Map 中是否有对应的 value,实现如下:

  其他方法的实现与 BeanWrapper 一致。

六、CollectionWrapper

  CollectionWrapper 封装了一个集合对象,如下:

  private Collection<Object> object;

  public CollectionWrapper(MetaObject metaObject, Collection<Object> object) {
    this.object = object;
  }

  但对接口方法的实现实际上都是抛出一个 UnsupportedOperationException 异常,表示不支持该操作。Collection 是一个接口,任何实现了该接口的类的实例都可以看成是一个 Collection 对象,实现接口的形式有多种多样,在JDK中的默认实现的类图如下:



  不同的实现,会导致集合中元素的存储形式和调用方法不一致,所以很难统一地实现 ObjectWrapper 接口中定义的与集合无关的方法,其他三个集合相关方法依赖集合接口的方法,实现如下:

  @Override
  public boolean isCollection() {
    return true;
  }

  @Override
  public void add(Object element) {
    object.add(element);
  }

  @Override
  public <E> void addAll(List<E> element) {
    object.addAll(element);
  }


七、ObjectWrapperFactory

  ObjectWrapperFactory 是创建 ObjectWrapper 对象的工厂接口,默认实现类为 DefaultObjectWrapper,接口定义如下:

public interface ObjectWrapperFactory {
   // 是否有对象对应的包装类
  boolean hasWrapperFor(Object object);
   // 创建对象对应的包装类
  ObjectWrapper getWrapperFor(MetaObject metaObject, Object object);
  
}

  DefaultObjectWrapperFactory 是默认接口实现类,默认为不支持创建对象对应的包装类,实际上就是不可用,源码如下:

public class DefaultObjectWrapperFactory implements ObjectWrapperFactory {

  @Override
  public boolean hasWrapperFor(Object object) {
    return false;
  }

  @Override
  public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {
    throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper.");
  }
}

  你可以会觉得有点疑惑,不可用为什么还会有这样的一个默认实现,实际上这时设计中一种很常见的做法,可以将其看成是一个接口API的一个占位,使用接口的代码面向接口方法编程,使用者可以按需插入自定义的实现类,而框架层代码不会受到影响。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,490评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,581评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,830评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,957评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,974评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,754评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,464评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,357评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,847评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,995评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,137评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,819评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,482评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,023评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,149评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,409评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,086评论 2 355