概述
为了降低反射使用门槛,MyBatis 内部封装了一个反射工具箱,其中包含了 MyBatis 自身常用的反射操作,MyBatis 其他模块只需要调用反射工具箱暴露的简洁 API 即可实现想要的反射功能。
反射工具箱的具体代码实现位于 org.apache.ibatis.reflection 包中。
Reflector
Reflector 是 MyBatis 反射模块的基础。要使用反射模块操作一个 Class,都会先将该 Class 封装成一个 Reflector 对象,在 Reflector 中缓存 Class 的元数据信息,这可以提高反射执行的效率。
public class Reflector {
private final Class<?> type;// 对应的Class类型
// 可读属性的名称集合,可读属性就是存在相应getter方法的属性,初始值为空数组
private final String[] readablePropertyNames;
// 可写属性的名称集合,可写属性就是存在相应setter方法的属性,初始值为空数组
private final String[] writablePropertyNames;
// 记录了属性相应的setter方法,key是属性名称,value是Invoker对象,它是对setter方法对应
// Method对象的封装
private final Map<String, Invoker> setMethods = new HashMap<>();
// 属性相应的getter方法集合,key是属性名称,value也是Invoker对象
private final Map<String, Invoker> getMethods = new HashMap<>();
// 记录了属性相应的setter方法的参数值类型,key是属性名称,value是setter方法的参数类型
private final Map<String, Class<?>> setTypes = new HashMap<>();
// 记录了属性相应的getter方法的返回值类型,key是属性名称,value是getter方法的返回值类型
private final Map<String, Class<?>> getTypes = new HashMap<>();
// 记录了默认构造方法
private Constructor<?> defaultConstructor;
// 记录了所有属性名称的集合
private Map<String, String> caseInsensitivePropertyMap = new HashMap<>();
public Reflector(Class<?> clazz) {
type = clazz;
// 查找clazz的默认构造方法(无参构造方法),具体实现是通过反射遍历所有构造方法
addDefaultConstructor(clazz);
// 处理clazz中的getter方法,填充getMethods集合和getTypes集合
addGetMethods(clazz);
// 处理clazz中的setter方法,填充setMethods集合和setTypes集合
addSetMethods(clazz);
// 处理没有getter/setter方法的字段
addFields(clazz);
// 根据getMethods/setMethods集合,初始化可读/写属性的名称集合
readablePropertyNames = getMethods.keySet().toArray(new String[0]);
writablePropertyNames = setMethods.keySet().toArray(new String[0]);
// 初始化caseInsensitivePropertyMap集合,其中记录了所有大写格式的属性名称
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
// 省略部分代码
}
1. 核心初始化流程
Reflector 的核心字段:
- type(Class<?> 类型):该 Reflector 对象封装的 Class 类型。
- readablePropertyNames、writablePropertyNames(String[] 类型):可读、可写属性的名称集合。
- getMethods、setMethods(Map<String, Invoker> 类型):可读、可写属性对应的 getter 方法和 setter 方法集合,key 是属性的名称,value 是一个 Invoker 对象。Invoker 是对 Method 对象的封装。
- getTypes、setTypes(Map<String, Class<?>> 类型):属性对应的 getter 方法返回值以及 setter 方法的参数值类型,key 是属性名称,value 是方法的返回值类型或参数类型。
- defaultConstructor(Constructor<?> 类型):默认构造方法。
- caseInsensitivePropertyMap(Map<String, String> 类型):所有属性名称的集合,记录到这个集合中的属性名称都是大写的。
在我们构造一个 Reflector 对象的时候,传入一个 Class 对象,通过解析这个 Class 对象,即可填充上述核心字段,整个核心流程大致可描述为如下。
- 用 type 字段记录传入的 Class 对象。
- 通过反射拿到 Class 类的全部构造方法,并进行遍历,过滤得到唯一的无参构造方法来初始化 defaultConstructor 字段。这部分逻辑在 addDefaultConstructor() 方法中实现。
- 读取 Class 类中的 getter方法,填充上面介绍的 getMethods 集合和 getTypes 集合。这部分逻辑在 addGetMethods() 方法中实现。
- 读取 Class 类中的 setter 方法,填充上面介绍的 setMethods 集合和 setTypes 集合。这部分逻辑在 addSetMethods() 方法中实现。
- 读取 Class 中没有 getter/setter 方法的字段,生成对应的 Invoker 对象,填充 getMethods 集合、getTypes 集合以及 setMethods 集合、setTypes 集合。这部分逻辑在 addFields() 方法中实现。
- 根据前面三步构造的 getMethods/setMethods 集合的 keySet,初始化 readablePropertyNames、writablePropertyNames 集合。
- 遍历构造的 readablePropertyNames、writablePropertyNames 集合,将其中的属性名称全部转化成大写并记录到 caseInsensitivePropertyMap 集合中。
2. 核心方法解析
addGetMethods() 方法和 addSetMethods() 方法,它们分别用来解析传入 Class 类中的 getter方法和 setter() 方法,两者的逻辑十分相似。
以 addGetMethods() 方法为例深入分析,其主要包括如下三个核心步骤。
private void addGetMethods(Class<?> clazz) {
Map<String, List<Method>> conflictingGetters = new HashMap<>();
// 定位所有方法信息,获取当前 Class 类以及父类中定义的所有方法的唯一签名,以及相应的Method对象。
Method[] methods = getClassMethods(clazz);
// 按照 Java 的规范,从步骤 1 中返回的 Method 数组中查找 getter方法,将其记录到 conflictingGetters 集合中。
// conflictingGetters集合(HashMap<String, List<Method>>()类型)的key为属性名称,value是该属性对应的getter方法集合。
Arrays.stream(methods)
.filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
resolveGetterConflicts(conflictingGetters);
}
第一步,获取方法信息。 这里会调用 getClassMethods() 方法获取当前 Class 类的所有方法的唯一签名(注意一下,这里同时包含继承自父类以及接口的方法),以及每个方法对应的 Method 对象。
在递归扫描父类以及父接口的过程中,会使用 Map<String, Method> 集合记录遍历到的方法,实现去重的效果,其中 Key 是对应的方法签名,Value 为方法对应的 Method 对象。生成的方法签名的格式如下:
返回值类型#方法名称:参数类型列表
例如,addGetMethods(Class) 方法的唯一签名是:
java.lang.String#addGetMethods:java.lang.Class
这里生成的方法签名是包含返回值的,可以作为该方法全局唯一的标识。
第二步,按照 Java 的规范,从上一步返回的 Method 数组中查找 getter 方法,将其记录到 conflictingGetters 集合中。 这里的 conflictingGetters 集合(HashMap<String, List>()类型)中的 Key 为属性名称,Value 是该属性对应的 getter 方法集合。
为什么一个属性会查找到多个 getter 方法呢?这主要是由于类间继承导致的,在子类中我们可以覆盖父类的方法,覆盖不仅可以修改方法的具体实现,还可以修改方法的返回值,getter 方法也不例外,这就导致在第一步中产生了两个签名不同的方法。
第三步,解决方法签名冲突。 这里会调用 resolveGetterConflicts() 方法对这种 getter 方法的冲突进行处理,处理冲突的核心逻辑其实就是比较 getter 方法的返回值,优先选择返回值为子类的 getter 方法
在 resolveGetterConflicts() 方法处理完上述 getter 方法冲突之后,会为每个 getter 方法创建对应的 MethodInvoker 对象,然后统一保存到 getMethods 集合中。同时,还会在 getTypes 集合中维护属性名称与对应 getter 方法返回值类型的映射。
在通过 addGetMethods() 和 addSetMethods() 方法,完成 Class 类中 getter/setter 方法的处理之后,会继续调用 addFields() 方法处理没有 getter/setter 方法的字段。
private void addFields(Class<?> clazz) {
// 获取clazz中定义的全部字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
// 当setMethods集合不包含同名属性时,将其记录到setMethods集合和setTypes集合
if (!setMethods.containsKey(field.getName())) {
// issue #379 - removed the check for final because JDK 1.5 allows
// modification of final fields through reflection (JSR-133). (JGB)
// pr #16 - final static can only be set by the classloader
int modifiers = field.getModifiers();
if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) {
// addSetField()方法的主要功能是填充setMethods集合和setTypes集合,
// 与addGetMethod()方法类似,不再贴出代码
addSetField(field);
}
}
if (!getMethods.containsKey(field.getName())) {
addGetField(field);
}
}
if (clazz.getSuperclass() != null) {
addFields(clazz.getSuperclass());
}
}
这里我们以处理没有 getter 方法的字段为例,addFields() 方法会为这些字段生成对应的 GetFieldInvoker 对象并记录到 getMethods 集合中,同时也会将属性名称和属性类型记录到 getTypes 集合中。
3. Invoker
在 Reflector 对象的初始化过程中,所有属性的 getter/setter 方法都会被封装成 MethodInvoker 对象,没有 getter/setter 的字段也会生成对应的 Get/SetFieldInvoker 对象。
public interface Invoker {
// 调用底层封装的Method方法或是读写指定的字段
Object invoke(Object target, Object[] args);
Class<?> getType(); // 返回属性的类型
}
Invoker 接口的继承关系:
MethodInvoker 是通过反射方式执行底层封装的 Method 方法(例如,getter/setter 方法)完成属性读写效果的,Get/SetFieldInvoker 是通过反射方式读写底层封装的 Field 字段,进而实现属性读写效果的。
4. ReflectorFactory
Reflector 初始化过程会有一系列的反射操作,为了提升 Reflector 的初始化速度,MyBatis 提供了 ReflectorFactory 这个工厂接口对 Reflector 对象进行缓存,其中最核心的方法是用来获取 Reflector 对象的 findForClass() 方法。
public interface ReflectorFactory {
boolean isClassCacheEnabled();
void setClassCacheEnabled(boolean classCacheEnabled);
Reflector findForClass(Class<?> type);
}
DefaultReflectorFactory 是 ReflectorFactory 接口的默认实现,它默认会在内存中维护一个 ConcurrentHashMap<Class<?>, Reflector> 集合(reflectorMap 字段)缓存其创建的所有 Reflector 对象。
public class DefaultReflectorFactory implements ReflectorFactory {
// 是否开启 Reflector 对象 缓存,默认开启
private boolean classCacheEnabled = true;
// 缓存其创建的所有 Reflector 对象
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<>();
public DefaultReflectorFactory() {
}
// 省略部分代码
}
在其 findForClass() 方法实现中,首先会根据传入的 Class 类查询 reflectorMap 缓存,如果查找到对应的 Reflector 对象,则直接返回;否则创建相应的 Reflector 对象,并记录到 reflectorMap 中缓存,等待下次使用。
@Override
public Reflector findForClass(Class<?> type) {
if (classCacheEnabled) {
// synchronized (type) removed see issue #461
return reflectorMap.computeIfAbsent(type, Reflector::new);
} else {
return new Reflector(type);
}
}
默认对象工厂
ObjectFactory 是 MyBatis 中的反射工厂,其中提供了两个 create() 方法的重载,我们可以通过两个 create() 方法创建指定类型的对象。
public interface ObjectFactory {
// 设置配置信息
default void setProperties(Properties properties) {
}
// 通过无参构造器创建指定类的对象
<T> T create(Class<T> type);
// 根据参数列表,从指定类型中选择合适的构造器创建对象
<T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs);
// 检测指定类型是否为集合类型,主要处理java.util.Collection及其子类
<T> boolean isCollection(Class<T> type);
}
DefaultObjectFactory 是 ObjectFactory 接口的默认实现,其 create() 方法底层是通过调用 instantiateClass() 方法创建对象的。instantiateClass() 方法会通过反射的方式根据传入的参数列表,选择合适的构造函数实例化对象。
除了使用 DefaultObjectFactory 这个默认实现之外,我们还可以在 mybatis-config.xml 配置文件中配置自定义 ObjectFactory 接口扩展实现类。
属性解析工具
reflection.property 包下的三个属性解析相关的工具类:
- PropertyTokenizer 工具类负责解析由“.”和“[]”构成的表达式。PropertyTokenizer 继承了 Iterator 接口,可以迭代处理嵌套多层表达式。
- PropertyCopier 是一个属性拷贝的工具类,提供了与 Spring 中 BeanUtils.copyProperties() 类似的功能,实现相同类型的两个对象之间的属性值拷贝,其核心方法是 copyBeanProperties() 方法。
- PropertyNamer 工具类提供的功能是转换方法名到属性名,以及检测一个方法名是否为 getter 或 setter 方法。
MetaClass
MetaClass 提供了获取类中属性描述信息的功能,底层依赖前面介绍的 Reflector,在 MetaClass 的构造方法中会将传入的 Class 封装成一个 Reflector 对象,并记录到 reflector 字段中,MetaClass 的后续属性查找都会使用到该 Reflector 对象。
public class MetaClass {
private final ReflectorFactory reflectorFactory;
private final Reflector reflector;
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
this.reflector = reflectorFactory.findForClass(type);
}
// 省略部分代码
}
MetaClass 中的 findProperty() 方法是实现属性查找的核心方法,它主要处理了“.”导航的属性查找,该方法会用前文介绍的 PropertyTokenizer 解析传入的 name 表达式,该表达式可能通过“.”导航多层,例如,order.deliveryAddress.customer.name。
MetaClass 会逐层处理这个表达式,首先通过 Order 类型对应的 Reflector 查找 deliveryAddress 属性,查找成功之后,根据 deliveryAddress 属性的类型(即 Address 类型)创建对应的 MetaClass 对象(以及底层的 Reflector 对象),再继续查找其中的 customer 属性,如此递归处理,直至最后查找到 Customer 中的 name 属性。这部分递归查找逻辑位于 MetaClass.buildProperty() 方法中。
private StringBuilder buildProperty(String name, StringBuilder builder) {
// 解析属性表达式
PropertyTokenizer prop = new PropertyTokenizer(name);
if (prop.hasNext()) { // 是否还有子表达式要处理
// 查找PropertyTokenizer解析得到的属性名称
String propertyName = reflector.findPropertyName(prop.getName());
if (propertyName != null) { // 追加属性名
builder.append(propertyName);
builder.append(".");
// 查找指定属性对应的Class,为该属性创建对应的MetaClass对象
MetaClass metaProp = metaClassForProperty(propertyName);
// 递归解析PropertyTokenizer解析得到的子表达式字段,并将解析结果添加到builder中保存
metaProp.buildProperty(prop.getChildren(), builder);
}
} else { // 递归出口
String propertyName = reflector.findPropertyName(name);
if (propertyName != null) {
builder.append(propertyName);
}
}
return builder;
}
在上述 MetaClass 查找属性的过程中,还会调用 hasGetter() 和 hasSetter() 方法负责判断属性表达式中指定的属性是否有对应的 getter/setter 方法。这两个方法也是先通过 PropertyTokenizer 解析传入的 name 表达式,然后进行递归查询,在递归查询中会依赖 Reflector.hasGetter() 方法查找前文介绍的 getMethods 集合或 setMethods 集合,查找属性对应的 getter/setter 方法。
MetaClass 中的其他方法实现也都大多是依赖 PropertyTokenizer 解析表达式,然后递归查找,查找过程会依赖 Reflector 的相关方法。
ObjectWrapper
MetaClass 中封装的是 Class 元信息,ObjectWrapper 封装的则是对象元信息。在 ObjectWrapper 中抽象了一个对象的属性信息,并提供了查询对象属性信息的相关方法,以及更新属性值的相关方法。
public interface ObjectWrapper {
// 如果ObjectWrapper中封装的是普通的Bean对象,则调用相应属性的相应getter方法,
// 如果封装的是集合类,则获取指定key或下标对应的value值
Object get(PropertyTokenizer prop);
// 如果ObjectWrapper中封装的是普通的Bean对象,则调用相应属性的相应setter方法,
// 如果封装的是集合类,则设置指定key或下标对应的value值
void set(PropertyTokenizer prop, Object value);
// 查找属性表达式指定的属性,第二个参数表示是否将name指定的属性名称修改为驼峰命名方式
String findProperty(String name, boolean useCamelCaseMapping);
// 查找可写属性的名称集合
String[] getGetterNames();
// 查找可读属性的名称集合
String[] getSetterNames();
// 解析属性表达式指定属性的setter方法的参数类型
Class<?> getSetterType(String name);
// 解析属性表达式指定属性的getter方法的返回值类型
Class<?> getGetterType(String name);
// 判断属性表达式指定属性是否有getter/setter方法
boolean hasSetter(String name);
boolean hasGetter(String name);
// 为属性表达式指定的属性创建相应的MetaObject对象
MetaObject instantiatePropertyValue(String name, PropertyTokenizer prop, ObjectFactory objectFactory);
// 封装的对象是否为Collection类型
boolean isCollection();
// 调用Collection对象的add()方法
void add(Object element);
// 调用Collection对象的addAll()方法
<E> void addAll(List<E> element);
}
ObjectWrapper 的实现类:
BaseWrapper 是 ObjectWrapper 接口的抽象实现,其中只有一个 MetaObject 类型的字段。BaseWrapper 为子类实现了 resolveCollection()、getCollectionValue() 和 setCollectionValue() 三个针对集合对象的处理方法。其中,resolveCollection() 方法会将指定属性作为集合对象返回,底层依赖 MetaObject.getValue()方法实现。getCollectionValue() 方法和 setCollectionValue() 方法会解析属性表达式的下标信息,然后获取/设置集合中的对应元素,这里解析属性表达式依然是依赖前面介绍的 PropertyTokenizer 工具类。
BeanWrapper 继承了 BaseWrapper 抽象类,底层除了封装了一个 JavaBean 对象之外,还封装了该 JavaBean 类型对应的 MetaClass 对象,以及从 BaseWrapper 继承下来的 MetaObject 对象。
在 get() 方法和 set() 方法实现中,BeanWrapper 会根据传入的属性表达式,获取/设置相应的属性值。以 get() 方法为例,首先会判断表达式中是否含有数组下标,如果含有下标,会通过 resolveCollection() 和 getCollectionValue() 方法从集合中获取相应元素;如果不包含下标,则通过 MetaClass 查找属性名称在 Reflector.getMethods 集合中相应的 GetFieldInvoker,然后调用 Invoker.invoke() 方法读取属性值。
CollectionWrapper 是 ObjectWrapper 接口针对 Collection 集合的一个实现,其中封装了Collection 集合对象,只有 isCollection()、add()、addAll() 方法以及从 BaseWrapper 继承下来的方法是可用的,其他方法都会抛出 UnsupportedOperationException 异常。
MapWrapper 是针对 Map 类型的一个实现。
MetaObject
ObjectWrapper 实现了读写对象属性值、检测getter/setter 等基础功能,在分析 BeanWrapper 等实现类时,我们可以看到其底层会依赖 MetaObject。在 MetaObject 中维护了一个 originalObject 字段指向被封装的 JavaBean 对象,还维护了该 JavaBean 对象对应的 ObjectWrapper 对象(objectWrapper 字段)。
public class MetaObject {
private final Object originalObject;
private final ObjectWrapper objectWrapper;
private final ObjectFactory objectFactory;
private final ObjectWrapperFactory objectWrapperFactory;
private final ReflectorFactory reflectorFactory;
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);
}
}
// 省略部分代码
}
MetaObject 和 ObjectWrapper 中关于类级别的方法,例如,hasGetter() 方法、hasSetter() 方法、findProperty() 方法等,都是直接调用 MetaClass 或 ObjectWrapper 的对应方法实现的。其他关于对象级别的方法,都是与 ObjectWrapper 配合实现,例如 MetaObject.getValue()/setValue() 方法等。
以 getValue() 方法为例,该方法首先根据 PropertyTokenizer 解析指定的属性表达式,如果该表达式是包含“.”导航的多级属性查询,则获取子表达式并为其对应的属性对象创建关联的 MetaObject 对象,继续递归调用 getValue() 方法,直至递归处理结束,递归出口会调用 ObjectWrapper.get() 方法获取最终的属性值。
在 MetaObject 中,setValue() 方法的核心逻辑与 getValue() 方法基本类似,也是递归查找。但是,其中有一个不同之处需要你注意:如果需要设置的最终属性值不为空时,在递归查找 setter() 方法的过程中会调用 ObjectWrapper.instantiatePropertyValue() 方法初始化递归过程中碰到的任意空对象,但如果碰到为空的集合元素,则无法通过该方法初始化。ObjectWrapper.instantiatePropertyValue() 方法实际上是依赖 ObjectFactory 接口的 create()方法(默认实现是 DefaultObjectFactory )创建相应类型的对象。
总结
反射工具箱中最核心、最底层的是 Reflector 类的核心实现。以及在 Reflector 基础之上提供的各种工具类,其中包括 ObjectFactory 工厂类、ObjectWrapper 包装类以及记录元数据的 MetaClass、MetaObject 。