美文网首页MyBatis源码剖析
[MyBatis源码分析 - 反射器模块 - 组件七] Obje

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

作者: 小胡_鸭 | 来源:发表于2020-10-25 17:07 被阅读0次

    一、简介

      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的一个占位,使用接口的代码面向接口方法编程,使用者可以按需插入自定义的实现类,而框架层代码不会受到影响。

    相关文章

      网友评论

        本文标题:[MyBatis源码分析 - 反射器模块 - 组件七] Obje

        本文链接:https://www.haomeiwen.com/subject/ettwmktx.html