MyBatis 基础支持层:反射工具模块

反射是 Java 中非常强大、非常灵活的一种机制。在面向对象的 Java 语言中,我们只能按照 publicprivate 等关键字的规范去访问一个 Java 对象的属性和方法,但反射机制可以让我们在运行时拿到任何 Java 对象的属性或方法。但凡事都有两面性,越是灵活、越是强大的工具,用起来的门槛就越高,反射亦如此。为了降低反射使用门槛,MyBatis 内部封装了一个反射工具模块,其中包含了 MyBatis 自身常用的反射操作,MyBatis 其他模块只需要调用反射工具模块暴露的简洁 API 即可实现想要的反射功能。

反射工具模块的具体代码实现位于 org.apache.ibatis.reflection 包中,接下来我们深入分析该模块的核心实现。

1 Reflector 类

Reflector 类是 MyBatis 反射模块的基础,要使用反射模块操作一个 Class,都会先将该 Class 封装成一个 Reflector 实例。Reflector 会缓存 Class 的元数据信息,一定程度上也能提高反射执行的效率。

1.1 核心初始化流程

既然涉及到反射相关的操作,就必须管理类的 FieldsMethods,这些信息都记录在它的核心字段中,如下所示:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 该 Reflector 对象封装的 Class 类型
Class<?> type;
// 可读属性的名称集合
String[] readablePropertyNames;
// 可写属性的名称集合
String[] writablePropertyNames;
/*
* 可读、可写属性对应的 getter 方法和 setter 方法集合
* key 是属性的名称,value 是一个 Invoker 对象
* Invoker 是对 Method 对象的封装
*/
Map<String, Invoker> setMethods = new HashMap<>();
Map<String, Invoker> getMethods = new HashMap<>();
/*
* 属性对应的 getter 方法返回值类型以及 setter 方法的参数值类型
* key 是属性的名称,value 具体类型
*/
Map<String, Class<?>> setTypes = new HashMap<>();
Map<String, Class<?>> getTypes = new HashMap<>();
// 默认构造方法
Constructor<?> defaultConstructor;
// 所有属性名称的集合,记录到这个集合中的属性名称都是大写的
Map<String, String> caseInsensitivePropertyMap = new HashMap<>();

在构造 Reflector 对象的时候,会传入一个 Class 对象,通过解析这个 Class 对象,即可填充上述字段,核心流程如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public Reflector(Class<?> clazz) {
  //1. 用 type 字段记录传入的 Class 对象
  type = clazz;
  //2. 通过反射拿到 Class 类的全部构造方法,并进行遍历,
  // 过滤得到唯一的无参构造方法来初始化 defaultConstructor 字段
  addDefaultConstructor(clazz);
  //3. 拿到 Class 及其父类的所有方法,包括私有方法
  Method[] classMethods = getClassMethods(clazz);
  if (isRecord(type)) {
    // 如果是 record 类型,只获取 getter 方法填充,
    //  record 是新版 JDK 14 开始支持的不变类型
    addRecordGetMethods(classMethods);
  } else { // 如果是普通类型
    //4. 读取 Class 类中的 getter 方法,
    //  填充上面介绍的 getMethods 集合和 getTypes 集合
    addGetMethods(classMethods);
    //5. 读取 Class 类中的 setter 方法,
    //  填充上面介绍的 setMethods 集合和 setTypes 集合
    addSetMethods(classMethods);
    //6. 读取 Class 中没有 getter/setter 方法的字段,生成对应的 Invoker 对象
    //   填充 getMethods 集合、getTypes 集合以及 setMethods 集合、setTypes 集合
    addFields(clazz);
  }
  /*
  * 根据前面三步构造的 getMethods/setMethods 集合的 keySet,
  * 初始化 readablePropertyNames、writablePropertyNames 集合
  */
  readablePropertyNames = getMethods.keySet().toArray(new String[0]);
  writablePropertyNames = setMethods.keySet().toArray(new String[0]);
  /*
  * 遍历构造的 readablePropertyNames、writablePropertyNames 集合,
  * 将其中的属性名称全部转化成大写并记录到 caseInsensitivePropertyMap 集合中
  */
  for (String propName : readablePropertyNames) {
    caseInsensitivePropertyMap
      .put(propName.toUpperCase(Locale.ENGLISH), propName);
  }
  for (String propName : writablePropertyNames) {
    caseInsensitivePropertyMap
      .put(propName.toUpperCase(Locale.ENGLISH), propName);
  }
}

1.2 核心方法解析

了解了初始化的核心流程之后,我们再继续深入分析 Reflector 的核心方法。

1.2.1 getClassMethods

在构造 Reflector 对象时,首先会通过 getClassMethods 方法获取 Class 及其父类的所有方法的 Method 对象:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private Method[] getClassMethods(Class<?> clazz) {
  /*
  * 使用 Map<String, Method> 集合记录遍历到的方法,实现去重的效果,
  *  其中 Key 是对应的方法签名,Value 为方法对应的 Method 对象
  */
  Map<String, Method> uniqueMethods = new HashMap<>();
  Class<?> currentClass = clazz;
  while (currentClass != null 
    && currentClass != Object.class) {//获取到 Object 停止
    // 添加当前类自身的非桥接方法
    addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());
    // 接口中的方法也需要遍历
    Class<?>[] interfaces = currentClass.getInterfaces();
    for (Class<?> anInterface : interfaces) {
      addUniqueMethods(uniqueMethods, anInterface.getMethods());
    }
    // 递归扫描父类
    currentClass = currentClass.getSuperclass();
  }
  // 只返回 Method
  Collection<Method> methods = uniqueMethods.values();
  return methods.toArray(new Method[0]);
}

1.2.2 addGetMethods & addSetMethods

addGetMethods()addSetMethods() 分别用来解析传入 Class 类中的 getter() 方法和 setter() 方法,两者的逻辑十分相似。这里我们以 addGetMethods() 为例分析:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
private void addGetMethods(Method[] methods) {
  Map<String, List<Method>> conflictingGetters = new HashMap<>();
  // 从参数传入的 Method 数组中查找 getter 方法,将其记录到 conflictingGetters
  Arrays.stream(methods)
    .filter(m -> m.getParameterTypes().length == 0 
      && PropertyNamer.isGetter(m.getName()))
    .forEach(m -> addMethodConflict(conflictingGetters, 
      PropertyNamer.methodToProperty(m.getName()), m));
  // 解决方法签名冲突
  resolveGetterConflicts(conflictingGetters);
}

该方法主要包括以下两个核心步骤:

  1. 按照 Java 的规范,从参数传入的 Method 数组中查找 getter 方法,将其记录到 conflictingGetters 集合中。这里的 conflictingGetters 集合(HashMap<String, List<Method>> 类型)中的 Key 为属性名称,Value 是该属性对应的 getter 方法集合。
    为什么一个属性会查找到多个 getter 方法呢?
    这是因为类之间会有继承,在子类中我们可以覆盖父类的方法,覆盖不仅可以修改方法的具体实现,还可以修改方法的返回值,getter 方法也不例外,这会导致在第一步中产生多个签名不同的方法。
  2. 解决方法签名冲突。这里会调用 resolveGetterConflicts() 方法对这种 getter 方法的冲突进行处理,处理冲突的核心逻辑其实就是比较 getter 方法的返回值,优先选择返回值为子类的 getter 方法。
    • resolveGetterConflicts() 方法处理完上述 getter 方法冲突之后,会为每个 getter 方法创建对应的 MethodInvoker 对象,然后统一保存到 getMethods 集合中。同时,还会在 getTypes 集合中维护属性名称与对应 getter 方法返回值类型的映射。

到这里,addGetMethods() 的核心逻辑就分析清楚了。

1.2.3 addFields

Reflector 的构造方法通过 addGetMethods()addSetMethods() 完成 Class 类中 getter/setter 方法的处理之后,会继续调用 addFields() 方法处理没有 getter/setter 方法的字段。以处理没有 getter 方法的字段为例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
private void addFields(Class<?> clazz) {
  Field[] fields = clazz.getDeclaredFields();
  for (Field field : fields) {
    // 没有 set 方法的字段
    if (!setMethods.containsKey(field.getName())) {
      int modifiers = field.getModifiers();
      //只添加非 final static 的,因为按照规范 final static 只能由类加载器设置
      if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
        addSetField(field);
      }
    }
    // 没有 get 方法的字段
    if (!getMethods.containsKey(field.getName())) {
      addGetField(field);
    }
  }
  // 如果存在父类,则递归遍历父类字段
  if (clazz.getSuperclass() != null) {
    addFields(clazz.getSuperclass());
  }
}

private void addGetField(Field field) {
  if (isValidPropertyName(field.getName())) {
    // 为字段生成 get 方法的 Invoker,并记录到 getMethods
    getMethods.put(field.getName(), new GetFieldInvoker(field));
    Type fieldType = TypeParameterResolver.resolveFieldType(field, type);
    // 记录返回值类型
    getTypes.put(field.getName(), typeToClass(fieldType));
  }
}
  • addFields() 方法会为这些字段生成对应的 GetFieldInvoker 对象并记录到 getMethods 集合中,同时也会将属性名称和属性类型记录到 getTypes 集合中。
  • 处理没有 setter 方法的字段也是相同的逻辑。

2 Invoker 接口

Reflector 对象的初始化过程中,所有属性的 getter/setter 方法都会被封装成 MethodInvoker 对象,没有 getter/setter 的字段也会生成对应的 Get/SetFieldInvoker 对象。我们先来看下 Invoker 接口的定义:

1
2
3
4
5
6
7
public interface Invoker {
  // 调用底层封装的 Method 方法或是读写指定的字段
  Object invoke(Object target, Object[] args) 
    throws IllegalAccessException, InvocationTargetException;
  // 返回属性的类型
  Class<?> getType();
}

Invoker 接口的继承关系如下图所示:

Invoker 接口继承关系图
  • MethodInvoker 是通过反射方式执行底层封装的 Method 方法(例如,getter/setter 方法)完成属性的读写效果;
  • GetFieldInvoker/SetFieldInvoker 是通过反射方式读写底层封装的 Field 字段,进而实现属性的读写效果;
  • AmbiguousMethodInvoker 用于封装模棱两可的 Method在执行 invoke 的时候直接抛异常

3 ReflectorFactory 工厂

通过上面的分析我们知道,Reflector 初始化过程会有一系列的反射操作,为了提升 Reflector 的初始化速度,MyBatis 提供了 ReflectorFactory 这个工厂接口对 Reflector 对象进行缓存:

1
2
3
4
5
public interface ReflectorFactory {
  boolean isClassCacheEnabled();
  void setClassCacheEnabled(boolean classCacheEnabled);
  Reflector findForClass(Class<?> type);
}
  • 其中最核心的方法是用来获取 Reflector 对象的 findForClass() 方法。

3.1 DefaultReflectorFactory 默认实现

DefaultReflectorFactoryReflectorFactory 接口的默认实现:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class DefaultReflectorFactory implements ReflectorFactory {
  //缓存其创建的所有 Reflector 对象
  private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();
  //...
  @Override
  public Reflector findForClass(Class<?> type) {
    if (classCacheEnabled) {
      // 首先会根据传入的 Class 类查询 reflectorMap 缓存,
      // 如果查找到对应的 `Reflector` 对象,则直接返回;
      // 否则创建相应的 Reflector 对象,并记录到 reflectorMap 中缓存,等待下次使用
      return MapUtil.computeIfAbsent(reflectorMap, type, Reflector::new);
    } else {
      return new Reflector(type);
    }
  }
}

它会在内存中维护一个 ConcurrentHashMap<Class<?>, Reflector> 集合缓存其创建的所有 Reflector 对象。

在其 findForClass() 方法实现中,首先会根据传入的 Class 类查询 reflectorMap 缓存,如果查找到对应的 Reflector 对象,则直接返回;否则创建相应的 Reflector 对象,并记录到 reflectorMap 中缓存,等待下次使用。

4 ObjectFactory 工厂

ObjectFactory 是 MyBatis 中的反射工厂,它提供了两个重载的 create() 方法,我们可以通过这两个 create() 方法创建指定类型的对象。

4.1 DefaultObjectFactory 默认实现

DefaultObjectFactoryObjectFactory 接口的默认实现,其核心方法是 create()

1
2
3
4
5
6
7
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  // 如果是常用接口类型,返回其常用实现类,例如 List 类型返回 ArrayList.class
  // 否则直接返回 type 自身
  Class<?> classToCreate = resolveInterface(type);
  // 通过得到的类型创建对象
  return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
}

该方法通过调用 instantiateClass() 创建对象:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private  <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
  try {
    Constructor<T> constructor;
    if (constructorArgTypes == null || constructorArgs == null) {
      // 如果没参数则调用无参构造创建对象
      constructor = type.getDeclaredConstructor();
      try {
        return constructor.newInstance();
      } // catch ...
    }
    // 否则利用参数构造对象
    constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0]));
    try {
      // 调用有参构造
      return constructor.newInstance(constructorArgs.toArray(new Object[0]));
    } catch (IllegalAccessException e) {
      // 如果创建失败,并且可以修改访问权限,则修改权限后再尝试创建实例
      if (Reflector.canControlMemberAccessible()) {
        constructor.setAccessible(true);
        return constructor.newInstance(constructorArgs.toArray(new Object[0]));
      } else {
        throw e;
      }
    }
  } //catch ...
}

instantiateClass() 会通过反射的方式根据传入的参数列表,选择合适的构造函数实例化对象。

5 property 解析工具

反射工具模块中的 property 解析工具主要用于对类的属性进行反射解析。

5.1 PropertyTokenizer

PropertyTokenizer 工具类负责解析由 .[] 构成的表达式。PropertyTokenizer 继承了 Iterator 接口,可以迭代处理嵌套多层表达式。

我们以 order[0].item[2].firstName 为例,构建一个测试用例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
PropertyTokenizer pt = new PropertyTokenizer("order[0].item[2].firstName");
System.out.println("name: " + pt.getName());
System.out.println("IndexedName: " + pt.getIndexedName());
System.out.println("Index: " + pt.getIndex());
System.out.println("children: " + pt.getChildren());
while (pt.hasNext()) {
  System.out.println("====================");
  pt = pt.next();
  System.out.println("name: " + pt.getName());
  System.out.println("IndexedName: " + pt.getIndexedName());
  System.out.println("Index: " + pt.getIndex());
  System.out.println("children: " + pt.getChildren());
}

结果如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
name: order
IndexedName: order[0]
Index: 0
children: item[2].firstName
====================
name: item
IndexedName: item[2]
Index: 2
children: firstName
====================
name: firstName
IndexedName: firstName
Index: null
children: null

5.2 PropertyCopier

PropertyCopier 是一个属性拷贝的工具类,提供了与 Spring 中 BeanUtils.copyProperties() 类似的功能,实现相同类型的两个对象之间的属性值拷贝,其核心方法是 copyBeanProperties()

5.3 PropertyNamer

PropertyNamer 工具类提供的功能是转换方法名到属性名,以及检测一个方法名是否为 gettersetter 方法。该工具类的重点是 methodToProperty() 方法,以下是该方法的解析规则:

1
2
3
4
5
6
7
8
9
//原始字符串 => 解析结果
isAaA => aaA
isAAA => AAA
isaaA => aaA
getAaA => aaA
setAaA => aaA
// 非 is、get、set 开头的字符串会抛出异常
org.apache.ibatis.reflection.ReflectionException: 
Error parsing property name 'xxxAaA'.  Didn't start with 'is', 'get' or 'set'.

6 MetaClass 类

MetaClass 提供了获取类中属性描述信息的功能,底层依赖前面介绍的 ReflectorPropertyTokenizerMetaClass 的构造方法会将传入的 Class 封装成一个 Reflector 对象并记录到 reflector 字段中,MetaClass 的后续属性查找都会使用到该 Reflector 对象:

1
2
3
4
5
6
7
8
9
//通过静态方法返回 MetaClass 对象
public static MetaClass forClass(Class<?> type, ReflectorFactory reflectorFactory) {
  return new MetaClass(type, reflectorFactory);
}
// 构造方法中通过 ReflectorFactory 获得 Class 的 Reflector 封装
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
  this.reflectorFactory = reflectorFactory;
  this.reflector = reflectorFactory.findForClass(type);
}

MetaClass 中的 findProperty() 方法是实现属性查找的核心方法,它主要处理了 . 导航和 [] 下标的属性查找:

1
2
3
4
public String findProperty(String name) {
  StringBuilder prop = buildProperty(name, new StringBuilder());
  return prop.length() > 0 ? prop.toString() : null;
}

该方法依赖 buildProperty

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private StringBuilder buildProperty(String name, StringBuilder builder) {
  // 解析 name 表达式
  PropertyTokenizer prop = new PropertyTokenizer(name);
  // children 不为空,表明还有下一级(存在 . )
  if (prop.hasNext()) {
    //拿到第一层的属性名
    String propertyName = reflector.findPropertyName(prop.getName());
    if (propertyName != null) {
      // 追加下一级
      builder.append(propertyName);
      builder.append(".");
      // 构建第一层属性的 MateClass
      MetaClass metaProp = metaClassForProperty(propertyName);
      // 递归子层级
      metaProp.buildProperty(prop.getChildren(), builder);
    }
  } else {
    // 如果只有一级,直接作为全名
    String propertyName = reflector.findPropertyName(name);
    if (propertyName != null) {
      builder.append(propertyName);
    }
  }
  return builder;
}

7 ObjectWrapper 接口

MetaClass 中封装的是 Class 元信息,对象元信息的封装则是由 ObjectWrapper 实现的。在 ObjectWrapper 中抽象了一个对象的属性信息,并提供了查询对象属性信息的相关方法,以及更新属性值的相关方法。

ObjectWrapper 的实现类如下图所示:

ObjectWrapper 接口继承关系图

7.1 BaseWrapper 抽象实现类

BaseWrapperObjectWrapper 接口的抽象实现类,其中只有一个 MetaObject 类型的字段。BaseWrapper 为子类实现了 resolveCollection()getCollectionValue()setCollectionValue() 三个针对集合对象的处理方法。

  • resolveCollection() 方法会将指定属性作为集合对象返回,底层依赖 MetaObject.getValue() 方法实现。
  • getCollectionValue() 方法和 setCollectionValue() 方法会解析属性表达式的下标信息,然后获取/设置集合中的对应元素,这里解析属性表达式依然是依赖前面介绍的 PropertyTokenizer 工具类。

7.2 BeanWrapper 实现类

BeanWrapper 继承了 BaseWrapper 抽象类,底层除了封装了一个 JavaBean 对象之外,还封装了该 JavaBean 类型对应的 MetaClass 对象,以及从 BaseWrapper 继承下来的 MetaObject 对象。

get() 方法和 set() 方法实现中,BeanWrapper 会根据传入的属性表达式,获取/设置相应的属性值。

get() 方法为例,首先会判断表达式中是否含有数组下标:

  • 如果含有下标,会通过 resolveCollection()getCollectionValue() 方法从集合中获取相应元素;
  • 如果不包含下标,则通过 MetaClass 查找属性名称在 Reflector.getMethods 集合中相应的 GetFieldInvoker,然后调用 Invoker.invoke() 方法读取属性值。

BeanWrapper 中其他方法的实现也大都与 get() 方法和 set() 方法类似,依赖 MetaClassMetaObject 完成相关对象中属性信息读写。

7.3 CollectionWrapper 实现类

CollectionWrapperObjectWrapper 接口针对 Collection 集合的一个实现,其中封装了 Collection<Object> 集合对象,其中只有 isCollection()add()addAll() 方法以及从 BaseWrapper 继承下来的方法是可用的,其他方法都会抛出 UnsupportedOperationException 异常。

7.4 MapWrapper 实现类

MapWrapper 是针对 Map 类型的一个实现,原理与 BeanWrapper 类似,就略过了。

8 MetaObject 类

通过对 ObjectWrapper 的介绍我们了解到,ObjectWrapper 实现了读写对象属性值、检测 getter/setter 等基础功能,在分析 BeanWrapper 等实现类时,我们可以看到其底层会依赖 MetaObject

MetaObject 中维护了一个 originalObject 字段指向被封装的 JavaBean 对象,还维护了该 JavaBean 对象对应的 ObjectWrapper 对象。

1
2
Object originalObject;
ObjectWrapper objectWrapper;

MetaObjectObjectWrapper 中关于类级别的方法,例如,hasGetter() 方法、hasSetter() 方法、findProperty() 方法等,都是直接调用 MetaClassObjectWrapper 的对应方法实现的:

1
2
3
4
5
6
7
8
9
public String findProperty(String propName, boolean useCamelCaseMapping) {
  return objectWrapper.findProperty(propName, useCamelCaseMapping);
}
public boolean hasSetter(String name) {
  return objectWrapper.hasSetter(name);
}
public boolean hasGetter(String name) {
  return objectWrapper.hasGetter(name);
}

其他关于对象级别的方法,都是与 ObjectWrapper 配合实现,例如 MetaObject.getValue()/setValue() 方法等,这里以 getValue() 方法为例,该方法首先根据 PropertyTokenizer 解析指定的属性表达式,如果该表达式是包含 . 导航的多级属性查询,则获取子表达式并为其对应的属性对象创建关联的 MetaObject 对象,继续递归调用 getValue() 方法,直至递归处理结束,递归出口会调用 ObjectWrapper.get() 方法获取最终的属性值。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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 {
      // 通过 MetaObject 获取 value
      return metaValue.getValue(prop.getChildren());
    }
  } else {
    return objectWrapper.get(prop);
  }
}

MetaObject 中,setValue() 方法的核心逻辑与 getValue() 方法基本类似,也是递归查找。但是,其中有一个不同之处需要注意:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
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) {
        return;
      } else {
        metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
      }
    }
    metaValue.setValue(prop.getChildren(), value);
  } else {
    objectWrapper.set(prop, value);
  }
}
  • 如果需要设置的最终属性值不为空时,在递归查找 setter() 方法的过程中会调用 ObjectWrapper.instantiatePropertyValue() 方法初始化递归过程中碰到的任意空对象。
    • ObjectWrapper.instantiatePropertyValue() 方法实际上是依赖 ObjectFactory 接口的 create() 方法(默认实现是 DefaultObjectFactory,上文介绍过了)创建相应类型的对象
  • 如果碰到为空的集合元素,则无法通过该方法初始化。


欢迎关注我的公众号,第一时间获取文章更新:

微信公众号

相关内容