美文网首页
MyBatis印象阅读之反射工具MetaObject

MyBatis印象阅读之反射工具MetaObject

作者: 向光奔跑_ | 来源:发表于2019-07-25 11:13 被阅读0次

    在上一节中,我们介绍了关于反射相关的TypeParameterResolver和MetaClass,今日来学习剩下的MetaObject

    可能大家已经注意到,我们上节中的MetaClass和本节中的MetaObject有点相像,它们之间是否会有关联?没错,如果说MetaClass是类的元信息的,那么MetaObject就是针对实例的。

    1.MetaObject类学习

    按照之前的学习方法,我们继续从一个Test Demo中来进行开始:

    
     //MetaObjectTest
    
      @Test
      void shouldGetAndSetField() {
        RichType rich = new RichType();
        MetaObject meta = SystemMetaObject.forObject(rich);
        meta.setValue("richField", "foo");
        assertEquals("foo", meta.getValue("richField"));
      }
    

    其中RichType为:

    
    public class RichType {
    
      private RichType richType;
    
      private String richField;
    
      private String richProperty;
    
      private Map richMap = new HashMap();
    
      private List richList = new ArrayList() {
        {
          add("bar");
        }
      };
      
      //其他是属性的get,set方法
     }
    

    上述中我们有一个方法我们还未接触过SystemMetaObject,那么我们来看下代码:

    public final class SystemMetaObject {
    
      public static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
      public static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
      public static final MetaObject NULL_META_OBJECT = MetaObject.forObject(NullObject.class, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
    
      private SystemMetaObject() {
        // Prevent Instantiation of Static Class
      }
    
      private static class NullObject {
      }
    
      public static MetaObject forObject(Object object) {
        return MetaObject.forObject(object, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
      }
    
    }
    
    

    注意,最关键的高潮来了

    //MetaObject
    
      public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
        if (object == null) {
          return SystemMetaObject.NULL_META_OBJECT;
        } else {
          return new MetaObject(object, objectFactory, objectWrapperFactory, reflectorFactory);
        }
      }
      
     /**
     * 注意这里的构造函数是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) {
          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);
        }
      }
    

    这里引入了BeanWrapper,我们来进入分析下。

    1.1 BeanWrapper源码分析

    我们先来看它的属性和构造方法:

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

    整体的我们都熟悉了一遍,那么我们回到最初的起点:

    
    
      @Test
      void shouldGetAndSetField() {
        RichType rich = new RichType();
        MetaObject meta = SystemMetaObject.forObject(rich);
        meta.setValue("richField", "foo");
        assertEquals("foo", meta.getValue("richField"));
      }
    
    

    这里调用的setValue方法我们来看下源码。

    1.2 MetaObject的setValue源码分析

      public void setValue(String name, Object value) {
        PropertyTokenizer prop = new PropertyTokenizer(name);
        if (prop.hasNext()) {
          //获取属性实例
          MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
          if (metaValue == SystemMetaObject.NULL_META_OBJECT) {
            if (value == null) {
              // don't instantiate child path if value is null
              return;
            } else {
               // 如果没有实例,帮他初始化一个实例
              metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
            }
          }
          metaValue.setValue(prop.getChildren(), value);
        } else {
          objectWrapper.set(prop, value);
        }
      }
    
    

    这里我们又看到了PropertyTokenizer,那就说明这里肯定又跟递归有关。

    这里整理下逻辑可以帮助大家阅读

    • 首先判断传参name是否存在递归,如果不存在则直接调用objectWrapper.set(prop, value);赋值
    • 如果存在递归,使用PropertyTokenizer解析,然后是通过metaObjectForProperty(prop.getIndexedName());获取实例下的属性实例,判断是否为SystemMetaObject.NULL_META_OBJECT,如果是,则判断要赋值的value是否为空,如果是空则直接返回,不用继续赋值,如果不是,通过 objectWrapper.instantiatePropertyValue(name, prop, objectFactory)初始化创建属性实例
    • 最后递归到最后一层属性时,通过 metaValue.setValue(prop.getChildren(), value)赋值

    接下来我们进入metaObjectForProperty方法:

      public MetaObject metaObjectForProperty(String name) {
        Object value = getValue(name);
        return MetaObject.forObject(value, objectFactory, objectWrapperFactory, reflectorFactory);
      }
    

    再来进入看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);
        }
      }
    

    再来看instantiatePropertyValue方法:

      /**
       * 初始化属性
       */
      @Override
      public MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory) {
        MetaObject metaValue;
        Class<?> type = getSetterType(prop.getName());
        try {
          Object newObject = objectFactory.create(type);
          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;
      }
    

    这里的逻辑大致就是创建实例里的某个属性的实例化。可能会有点绕。。这部分的代码强力建议调试下MetaObjectTest的shouldGetAndSetNestedField测试方法。

    最后是 objectWrapper.set(prop, value)我们来看下代码:

      @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);
        }
      }
    
      private void setBeanProperty(PropertyTokenizer prop, Object object, Object value) {
        try {
          Invoker method = metaClass.getSetInvoker(prop.getName());
          Object[] params = {value};
          try {
            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);
        }
      }
    
    

    逻辑也很清晰,就是调用反射给实例属性赋值。

    2.今日总结

    今天我们介绍了MetaObject设置属性的过程,从中又涉及到了递归操作,希望大家可以通过调试并结合讲解来分析这块代码。

    相关文章

      网友评论

          本文标题:MyBatis印象阅读之反射工具MetaObject

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