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

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

reflection.Reflector

Reflector 类是 MyBatis 反射模块的基础。在 reflection 模块操作一个 Class 之前,需要先将该 Class 封装成一个 Reflector 实例。Reflector 缓存了 Class 的元数据信息,从而在一定程度上提高了反射执行的效率。

核心初始化流程

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

  public class Reflector {
  ...
  // 该 Reflector 对象封装的 Class 类型
  private final Class<?> type;
  // 可读属性的名称集合
  private final String[] readablePropertyNames;
  // 可写属性的名称集合
  private final String[] writablePropertyNames;
  /*
   * 可读、可写属性对应的 getter 方法和 setter 方法集合
   * key 是属性的名称,value 是一个 Invoker 对象
   * Invoker 是对 Method 对象的封装
   */
  private final Map<String, Invoker> setMethods = new HashMap<>();
  private final Map<String, Invoker> getMethods = new HashMap<>();
  /*
   * 属性对应的 getter 方法返回值类型以及 setter 方法的参数值类型
   * key 是属性的名称,value 具体类型
   */
  private final Map<String, Class<?>> setTypes = new HashMap<>();
  private final Map<String, Class<?>> getTypes = new HashMap<>();
  // 默认构造方法
  private Constructor<?> defaultConstructor;
  // 所有属性名称的集合,记录到这个集合中的属性名称都是大写的
  private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
  ...
}
  

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

  public Reflector(Class<?> clazz) {
  //1. 用 private final Class<?> 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);
  }
}
  

核心方法解析

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

getClassMethods

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

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

addGetMethods & addSetMethods

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

  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 方法集合。
  2. 解决方法签名冲突。这里会调用 resolveGetterConflicts() 方法对这种 getter 方法的冲突进行处理,处理冲突的核心逻辑其实就是比较 getter 方法的返回值,优先选择返回值为子类的 getter 方法。
    • resolveGetterConflicts() 方法处理完上述 getter 方法冲突之后,会为每个 getter 方法创建对应的 MethodInvoker 对象,然后统一保存到 getMethods 集合中。同时,还会在 getTypes 集合中维护属性名称与对应 getter 方法返回值类型的映射。

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

addFields

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

  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 方法的字段也是相同的逻辑。

reflection.invoker.Invoker

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

  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 的时候直接抛异常

reflection.ReflectorFactory

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

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

DefaultReflectorFactory

DefaultReflectorFactoryReflectorFactory 接口的默认实现:

  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 中缓存,等待下次使用。

reflection.factory.ObjectFactory

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

DefaultObjectFactory

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

  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() 创建对象:

  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() 会通过反射的方式根据传入的参数列表,选择合适的构造函数实例化对象。

reflection.property 解析工具

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

PropertyTokenizer

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

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

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

结果如下:

  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
  

PropertyCopier

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

PropertyNamer

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

  //原始字符串 => 解析结果
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'.
  

reflection.MetaClass

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

  //通过静态方法返回 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() 方法是实现属性查找的核心方法,它主要处理了 . 导航和 [] 下标的属性查找:

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

该方法依赖 buildProperty

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

reflection.wrapper.ObjectWrapper

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

ObjectWrapper 的实现类如下图所示:

ObjectWrapper 接口继承关系图

BaseWrapper 抽象实现类

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

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

BeanWrapper 实现类

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

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

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

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

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

CollectionWrapper 实现类

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

MapWrapper 实现类

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

reflection.MetaObject

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

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

  Object originalObject;
ObjectWrapper objectWrapper;
  

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

  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() 方法获取最终的属性值。

  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() 方法基本类似,也是递归查找。但是,其中有一个不同之处需要注意:

  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,上文介绍过了)创建相应类型的对象
  • 如果碰到为空的集合元素,则无法通过该方法初始化。