orm

mybatis的反射工具类—MetaObject(反射对象类)

2021-01-24  本文已影响0人  小胖学编程

mybatis提供了一个底层的反射工具类。我们在业务代码中也可以使用。

1. 相关API的使用

MeatObject是Mybatis的工具类,通过MetaObject获取和设置对象的属性值。

public class TestMetaObject {
    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();

    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();

    public static void main(String[] args) {

        //第一次读取
        Teca teca = new Teca();

        List<Teca.Stu> objects = new ArrayList<>();
        objects.add(new Teca.Stu());
        teca.setName("lili");
        teca.setStus(objects);

        MetaObject metaObject = SystemMetaObject.forObject(teca);
        System.out.println("getGetterNames:" + JSON.toJSONString(metaObject.getGetterNames()));
        System.out.println("getSetterNames:" + JSON.toJSONString(metaObject.getSetterNames()));
        System.out.println("name的get方法返回值:" + JSON.toJSONString(metaObject.getGetterType("name")));
        System.out.println("stus的set方法参数值:" + JSON.toJSONString(metaObject.getGetterType("stus")));
        System.out.println("name的hasGetter:" + metaObject.hasGetter("name"));
        //出现:UnsupportedOperationException异常
//        System.out.println("stus.id(属性为集合)的hasGetter:" + metaObject.hasGetter("stus.id"));
        System.out.println("stu.id(属性为对象)的hasGetter:" + metaObject.hasGetter("stu.id"));
        System.out.println("获取name的属性值:" + metaObject.getValue("name"));
        //重新设置属性值
        metaObject.setValue("name","huahua");
        System.out.println("设置name的属性值:" + metaObject.getValue("name"));
        //设置属性(集合)的元素值
        metaObject.setValue("stus[0].id","001");
        System.out.println("获取stus集合的第一个元素的属性值:"+JSON.toJSONString(metaObject.getValue("stus[0].id")));
        System.out.println("对象的序列化:"+JSON.toJSONString(teca));
    }
}

返回结果:

getGetterNames:["stu","price","name","stus"]
getSetterNames:["stu","price","name","stus"]
name的get方法返回值:"java.lang.String"
stus的set方法参数值:"java.util.List"
name的hasGetter:true
stu.id(属性为对象)的hasGetter:true
获取name的属性值:lili
设置name的属性值:huahua
获取stus的第一个元素的属性值:"001"
对象的序列化:{"name":"huahua","price":0.0,"stus":[{"id":"001"}]}

基本的pojo对象:

@Data
@ToString
public class Teca {

    private String name;

    private double price;

    private List<Stu> stus;

    private Stu stu;

    @Data
    public static class Stu {
        private String id;
    }
}

2. 参数说明

参数 说明 默认使用值
object 待处理的原始对象 业务对象
objectFactory 创建对象的工厂 new DefaultObjectFactory()
objectWrapperFactory object是否是wrapper new DefaultObjectWrapperFactory()
reflectorFactory 反射的工厂类 new DefaultReflectorFactory()

2.1 注意事项:

MetaObject提供了一个工具类:

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());
  }

}

当调用SystemMetaObject获取MetaObject对象时,每次均new DefaultReflectorFactory()了一个反射工厂类。

继续查看:

public class DefaultReflectorFactory implements ReflectorFactory {
  private boolean classCacheEnabled = true;
  private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();

  public DefaultReflectorFactory() {
  }

  @Override
  public boolean isClassCacheEnabled() {
    return classCacheEnabled;
  }

  @Override
  public void setClassCacheEnabled(boolean classCacheEnabled) {
    this.classCacheEnabled = classCacheEnabled;
  }

  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {
            // synchronized (type) removed see issue #461
      Reflector cached = reflectorMap.get(type);
      if (cached == null) {
        cached = new Reflector(type);
        reflectorMap.put(type, cached);
      }
      return cached;
    } else {
      return new Reflector(type);
    }
  }

}

这个类并没有做很复杂的操作,就是获取object的type类型,且存储到Map中。当同一个object调用forObject()时,后续调用可以在缓存中获取反射对象。

但注意:SystemMetaObject方法因为每次均new了一个新的DefaultReflectorFactory工厂。若每次在方法中调用SystemMetaObject.forObject获取MetaObject对象。同一个对象可能不会走缓存。

推荐做法:DefaultReflectorFactory声明为静态变量:

public class TestMetaObject {
    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();

    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();

    public static void main(String[] args) {
        
        //第一次读取
        Teca teca = new Teca();

        List<Teca.Stu> objects = new ArrayList<>();
        objects.add(new Teca.Stu());
        teca.setName("lili");
        teca.setStus(objects);
        MetaObject metaObject = MetaObject.forObject(teca, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);
    }
}

3. 源码分析

在创建MetaObject对象的时候,就会将传入的Object对象的所有反射对象都缓存起来。后续调用metaObject.setValue等方法时,直接在缓存中获取到反射对象,然后执行反射操作。

3.1 创建MetaObject对象

创建MetaObject对象时,选择不同的Wrapper进行包装。

  private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
    this.originalObject = object;
    this.objectFactory = objectFactory;
    this.objectWrapperFactory = objectWrapperFactory;
    this.reflectorFactory = reflectorFactory;

   //判断传入的对象是否是ObjectWrapper子类
    if (object instanceof ObjectWrapper) {
      this.objectWrapper = (ObjectWrapper) object;
    } else if (objectWrapperFactory.hasWrapperFor(object)) {  //默认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) {  //判断是否是集合
      this.objectWrapper = new CollectionWrapper(this, (Collection) object);
    } else {   //普通的pojo处理
      this.objectWrapper = new BeanWrapper(this, object);
    }
  }

以普通的pojo为例,创建BeanWrapper对象,在forClass方法中会遍历object的所有方法。获取反射对象。

  public BeanWrapper(MetaObject metaObject, Object object) {
    super(metaObject);
    this.object = object;
    //getReflectorFactory为传入的参数。
    this.metaClass = MetaClass.forClass(object.getClass(), metaObject.getReflectorFactory());
  }

到此处:

  private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
    this.reflectorFactory = reflectorFactory;
    this.reflector = reflectorFactory.findForClass(type);
  }

到达此处,判断缓存中是否存在某对象的解析结果:

注意:MetaObject.forObject会将object结果缓存起来,后续在使用MetaObject.forObject创建对象时,直接在缓存中获取。

  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {
            // synchronized (type) removed see issue #461
      Reflector cached = reflectorMap.get(type);
      if (cached == null) {
        cached = new Reflector(type);
        reflectorMap.put(type, cached);
      }
      return cached;
    } else {
      return new Reflector(type);
    }
  }

若没有存在,则调用new Reflector(type);去创建cache对象。

  public Reflector(Class<?> clazz) {
    type = clazz;
     //解析构造函数
    addDefaultConstructor(clazz);
    //解析get方法
    addGetMethods(clazz);
    //解析set方法
    addSetMethods(clazz);
    //解析Field属性
    addFields(clazz);
    readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
    writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
    for (String propName : readablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
    for (String propName : writeablePropertyNames) {
      caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
    }
  }

3.2 解析object所有的set方法

总方法:

  private void addSetMethods(Class<?> cls) {
    Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
    //获取到object所有的方法。
    Method[] methods = getClassMethods(cls);
    //遍历所有Method
    for (Method method : methods) {
      String name = method.getName();
      //判断name是否以set开头
      if (name.startsWith("set") && name.length() > 3) {
        //set方法的参数只有一个
        if (method.getParameterTypes().length == 1) {
          //移除set前缀。且均变成小写
          name = PropertyNamer.methodToProperty(name);
          //将method和name存储到Map集合
          addMethodConflict(conflictingSetters, name, method);
        }
      }
    }
    //将conflictingSetters放入到属性字段(缓存)
    resolveSetterConflicts(conflictingSetters);
  }

2. 删除方法的前缀,获取属性名:

  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;
  }

3. key为属性名,value是集合:

  private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
    List<Method> list = conflictingMethods.get(name);
    if (list == null) {
      list = new ArrayList<Method>();
      conflictingMethods.put(name, list);
    }
    list.add(method);
  }

4. 将局部变量放入到属性集合中:

  private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
    //遍历缓存的set方法集合
    for (String propName : conflictingSetters.keySet()) {
      List<Method> setters = conflictingSetters.get(propName);
      Method firstMethod = setters.get(0);
      //若是
      if (setters.size() == 1) {
        addSetMethod(propName, firstMethod);
      } else {
        //期待的type为对应get方法的返回值。
        Class<?> expectedType = getTypes.get(propName);
        if (expectedType == null) {
          throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
              + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " +
              "specification and can cause unpredicatble results.");
        } else {  //conflictingSetters一个name存在多个value时
          Iterator<Method> methods = setters.iterator();
          Method setter = null;
          
          while (methods.hasNext()) {
            Method method = methods.next();
            //寻找:方法只有一个参数,且参数是期待的参数(getXxx方法的返回值)。
            if (method.getParameterTypes().length == 1
                && expectedType.equals(method.getParameterTypes()[0])) {
              setter = method;
              break;
            }
          }
          if (setter == null) {
            throw new ReflectionException("Illegal overloaded setter method with ambiguous type for property "
                + propName + " in class " + firstMethod.getDeclaringClass() + ".  This breaks the JavaBeans " +
                "specification and can cause unpredicatble results.");
          }
          addSetMethod(propName, setter);
        }
      }
    }
  }

5. 放入到属性变量中

  private void addSetMethod(String name, Method method) {
    if (isValidPropertyName(name)) {
      setMethods.put(name, new MethodInvoker(method));
      Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
      setTypes.put(name, typeToClass(paramTypes[0]));
    }
  }

3.3 反射设置值

public class TestMetaObject {
    private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();

    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();

    public static void main(String[] args) {

        //第一次读取
        Teca teca = new Teca();
        MetaObject metaObject = MetaObject.forObject(teca, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);

        //重新设置属性值
        metaObject.setValue("name","huahua");
    }
}

设置分词器:

  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 && prop.getChildren() != null) {
          // don't instantiate child path if value is null
          return;
        } else {
          metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
        }
      }
      metaValue.setValue(prop.getChildren(), value);
    } else {
      //object的包装器
      objectWrapper.set(prop, value);
    }
  }
分词器的bug.png
  @Override
  public void set(PropertyTokenizer prop, Object value) {
    if (prop.getIndex() != null) {
      Object collection = resolveCollection(prop, object);
      setCollectionValue(prop, collection, value);
    } else {
      //分词器对象、object对象,设置的值
      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);
    }
  }

推荐阅读

https://www.cnblogs.com/javadeveloper/archive/2004/01/13/13151682.html

上一篇下一篇

猜你喜欢

热点阅读