MyBatis
在进行参数处理、结果映射时等操作时,会涉及大量的反射操作。为了简化这些反射相关操作,MyBatis
在 org.apache.ibatis.reflection
包下提供了专门的反射模块,对反射操作做了近一步封装,提供了更为简洁的 API
。
一、JavaBean规范
JavaBean 具有如下特征:
- 所有的属性都是私有的(通过 getter 和 setter 访问)
- 拥有公有的无参构造函数
- 提供 setter/getter
- 实现 Serializable 接口
二、Reflector 和 ReflectorFactory
2.1 Reflector
出于性能方面的考虑,Mybatis 不是等到使用的时候去解析 XML/反射类,而是为每一个类提供了反射器类 Reflector ,该类中存储了反射需要使用的类的元信息。
MyBatis
提供 Reflector
类来缓存类的字段名和 getter/setter 方法的元信息,使得反射时有更好的性能。使用方式是将原始类对象传入其构造方法,生成 Reflector
对象。
Reflector 是 MyBatis 中反射模块的基础,每个 Reflector 对象都对应一个类,在 Reflector 中 缓存了反射操作需要使用的类的元信息。 Reflector 中各个字段的含义如下:
private final Class<?> type;//对于class的类型
//可读属性的名称集合,存在get方法即可读
private final String[] readablePropertyNames;
//可写属性的名称集合,存在set方法即可写
private final String[] writablePropertyNames;
//记录了属性相应的 setter 方法, key 是属性名称, value 是 Invoker 对象,它是对 setter 方法对应
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<>();
在 Reflector 的构造方法中会解析指定的 Class 对象,并填充上述集合,具体实现如下:
public Reflector(Class<?> clazz) {
type = clazz;//初始化type对象
//查找 clazz 的默认构造方法(元参构造方法),具体实现是通过反射遥历所有构造方法,代码并不复杂
addDefaultConstructor(clazz);
//处理clazz中的get方法信息,填充getMethods、getTypes
addGetMethods(clazz);
//处理clazz中的set方法信息,填充setMethods、setTypes
addSetMethods(clazz);
//处理没有get、set方法的属性
addFields(clazz);
//根据get、set方法初始化可读属性集合和可写属性集合
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);
}
}
从上面类的属性中,我们可以看出:
一个反射器 Reflector 对应着一个 Class 对象
记录了默认构造函数
其余的是属性及其 setter/gettter 相关
对于一个属性(只有有 setter/getter 才能被称之为属性)
(1)如果是可读的(有 getter 方法),则 Reflector 会将其及其方法处理后放入对应的可读相关的集合中;
(2)如果是可写的(有 setter 方法),则 Reflector 会将其及其方法处理后放入对应的可写相关的集合中;
(3)最后使用 Map<String, String> caseInsensitivePropertyMap 记录所有的属性。
addGetMethods
和 addSetMethods
分别获取类的所有方法,从符合 getter/setter
规范的方法中解析出字段名,并记录方法的参数类型、返回值类型等信息:
private void addGetMethods(Class<?> clazz) {
//字段名-get方法
Map<String, List<Method>> conflictingGetters = new HashMap<>();
//获取类的所有方法,及其实现接口的方法,并根据方法签名去重
Method[] methods = getClassMethods(clazz);
//取get 方法 条件是入参为空,且已get开头
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 0 && PropertyNamer.isGetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingGetters, PropertyNamer.methodToProperty(m.getName()), m));
resolveGetterConflicts(conflictingGetters);
}
public static boolean isGetter(String name) {
return (name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2);
}
private void addSetMethods(Class<?> clazz) {
Map<String, List<Method>> conflictingSetters = new HashMap<>();
Method[] methods = getClassMethods(clazz);
Arrays.stream(methods).filter(m -> m.getParameterTypes().length == 1 && PropertyNamer.isSetter(m.getName()))
.forEach(m -> addMethodConflict(conflictingSetters, PropertyNamer.methodToProperty(m.getName()), m));
resolveSetterConflicts(conflictingSetters);
}
对 getter/setter
方法进行去重是通过类似 java.lang.String#getSignature:java.lang.reflect.Method
的方法签名来实现的,如果子类在实现过程中,参数、返回值使用了不同的类型(使用原类型的子类),则会导致方法签名不一致,同一字段就会对应不同的 getter/setter
方法,因此需要进行去重
private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) {
for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) {
Method winner = null;
// 属性名
String propName = entry.getKey();
for (Method candidate : entry.getValue()) {
if (winner == null) {
winner = candidate;
continue;
}
// 字段对应了多个get方法
Class<?> winnerType = winner.getReturnType();
Class<?> candidateType = candidate.getReturnType();
if (candidateType.equals(winnerType)) {
// 返回值类型相同
if (!boolean.class.equals(candidateType)) {
throw new ReflectionException(
"Illegal overloaded getter method with ambiguous type for property "
+ propName + " in class " + winner.getDeclaringClass()
+ ". This breaks the JavaBeans specification and can cause unpredictable results.");
} else if (candidate.getName().startsWith("is")) {
// 返回值为boolean的get方法可能有多个,如getIsSave和isSave,优先取is开头的
winner = candidate;
}
} else if (candidateType.isAssignableFrom(winnerType)) {
// OK getter type is descendant
// 可能会出现接口中的方法返回值是List,子类实现方法返回值是ArrayList,使用子类返回值方法
} else if (winnerType.isAssignableFrom(candidateType)) {
winner = candidate;
} else {
throw new ReflectionException(
"Illegal overloaded getter method with ambiguous type for property "
+ propName + " in class " + winner.getDeclaringClass()
+ ". This breaks the JavaBeans specification and can cause unpredictable results.");
}
}
// 记录字段名对应的get方法对象和返回值类型
addGetMethod(propName, winner);
}
}
去重的方式是使用更规范的方法以及使用子类的方法。在确认字段名对应的唯一 getter/setter
方法后,记录方法名对应的方法、参数、返回值等信息。MethodInvoker
可用于调用 Method
类的 invoke
方法来执行 getter/setter
方法(addSetMethods
记录映射关系的方式与 addGetMethods
大致相同)。
private void