Reflection 反射模块
本为介绍 MyBatis 的 reflection 反射工具模块,包括 Reflector、Invoker、ReflectorFactory 工厂、DefaultReflectorFactory、ObjectFactory 工厂、DefaultObjectFactory、PropertyTokenizer、MetaClass 等。
反射是 Java 中非常强大、非常灵活的一种机制。在面向对象的 Java 语言中,我们只能按照 public
、private
等关键字的规范去访问一个 Java 对象的属性和方法,但反射机制可以让我们在运行时拿到任何 Java 对象的属性或方法。但凡事都有两面性,越是灵活、越是强大的工具,用起来的门槛就越高,因此,MyBatis 内部封装了一个反射工具模块。该模块内部包含了 MyBatis 自身常用的反射操作,其他模块中的组件只需要调用 reflection 模块暴露的简单易用的 API 即可完成想要的反射操作。
reflection 模块的具体代码实现位于 org.apache.ibatis.reflection
包中,接下来我们深入分析该模块的核心实现。
reflection.Reflector
Reflector
类是 MyBatis 反射模块的基础。在 reflection 模块操作一个 Class 之前,需要先将该 Class 封装成一个 Reflector
实例。Reflector
缓存了 Class 的元数据信息,从而在一定程度上提高了反射执行的效率。
核心初始化流程
既然涉及到反射相关的操作,就必须管理类的 Fields
和 Methods
,这些信息都记录在它的核心字段中,如下所示:
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);
}
该方法主要包括以下两个核心步骤:
- 按照 Java 的规范,从参数传入的
Method
数组中查找 getter 方法,将其记录到conflictingGetters
集合中。这里的conflictingGetters
集合(HashMap<String, List<Method>>
类型)中的Key
为属性名称,Value
是该属性对应的getter
方法集合。这是因为类之间会有继承,在子类中我们可以覆盖父类的方法,覆盖不仅可以修改方法的具体实现,还可以修改方法的返回值,`getter` 方法也不例外,这会导致在第一步中产生多个签名不同的方法。 - 解决方法签名冲突。这里会调用
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 接口继承关系图](images/mybatis-invoker.webp)
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
DefaultReflectorFactory
是 ReflectorFactory
接口的默认实现:
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
DefaultObjectFactory
是 ObjectFactory
接口的默认实现,其核心方法是 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
工具类提供的功能是转换方法名到属性名,以及检测一个方法名是否为 getter
或 setter
方法。该工具类的重点是 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
提供了获取类中属性描述信息的功能,底层依赖前面介绍的 Reflector
和 PropertyTokenizer
,MetaClass
的构造方法会将传入的 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 接口继承关系图](images/mybatis-objectwrapper.webp)
BaseWrapper 抽象实现类
BaseWrapper
是 ObjectWrapper
接口的抽象实现类,其中只有一个 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()
方法类似,依赖 MetaClass
、MetaObject
完成相关对象中属性信息读写。
CollectionWrapper 实现类
CollectionWrapper
是 ObjectWrapper
接口针对 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;
MetaObject
和 ObjectWrapper
中关于类级别的方法,例如,hasGetter()
方法、hasSetter()
方法、findProperty()
方法等,都是直接调用 MetaClass
或 ObjectWrapper
的对应方法实现的:
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
,上文介绍过了)创建相应类型的对象
- 如果碰到为空的集合元素,则无法通过该方法初始化。