本文主要介绍MyBatis的反射模块是如何实现的。
DefaultReflectorFactory 源码分析
基础支持层反射模块Reflector/ReflectorFactory,DefaultReflectorFactory 用于创建 Reflector,同时兼有缓存的功能,它的源码如下。
public class DefaultReflectorFactory implements ReflectorFactory {
private boolean classCacheEnabled = true;
/** 目标类和反射器映射缓存 */
private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>();
// 省略部分代码
@Override
public Reflector findForClass(Class<?> type) {
// classCacheEnabled 默认为 true
if (classCacheEnabled) {
// 从缓存中获取 Reflector 对象
Reflector cached = reflectorMap.get(type);
// 缓存为空,则创建一个新的 Reflector 实例,并放入缓存中
if (cached == null) {
cached = new Reflector(type);
// 将 <type, cached> 映射缓存到 map 中,方便下次取用
reflectorMap.put(type, cached);
}
return cached;
} else {
// 创建一个新的 Reflector 实例
return new Reflector(type);
}
}
}
如上,DefaultReflectorFactory 的findForClass方法逻辑不是很复杂,包含两个访存操作,和一个对象创建操作。代码注释的比较清楚了,就不多说了。接下来,来分析一下反射器 Reflector。
Reflector 源码分析
Reflector 这个类的用途主要是是通过反射获取目标类的 getter 方法及其返回值类型,setter 方法及其参数值类型等元信息。并将获取到的元信息缓存到相应的集合中,供后续使用。
我将会分析三部分逻辑,分别如下:
- Reflector 构造方法及成员变量分析
- getter 方法解析过程
- setter 方法解析过程
Reflector 构造方法及成员变量分析
Reflector 构造方法中包含了很多初始化逻辑,目标类的元信息解析过程也是在构造方法中完成的,这些元信息最终会被保存到 Reflector 的成员变量中。下面我们先来看看 Reflector 的构造方法和相关的成员变量定义,代码如下:
public class Reflector {
private final Class<?> type;
private final String[] readablePropertyNames;
private final String[] writeablePropertyNames;
private final Map<String, Invoker> setMethods = new HashMap<String, Invoker>();
private final Map<String, Invoker> getMethods = new HashMap<String, Invoker>();
private final Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>();
private final Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>();
private Constructor<?> defaultConstructor;
private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>();
public Reflector(Class<?> clazz) {
type = clazz;
// 解析目标类的默认构造方法,并赋值给 defaultConstructor 变量
addDefaultConstructor(clazz);
// 解析 getter 方法,并将解析结果放入 getMethods 中
addGetMethods(clazz);
// 解析 setter 方法,并将解析结果放入 setMethods 中
addSetMethods(clazz);
// 解析属性字段,并将解析结果添加到 setMethods 或 getMethods 中
addFields(clazz);
// 从 getMethods 映射中获取可读属性名数组
readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]);
// 从 setMethods 映射中获取可写属性名数组
writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]);
// 将所有属性名的大写形式作为键,属性名作为值,存入到 caseInsensitivePropertyMap 中
for (String propName : readablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
for (String propName : writeablePropertyNames) {
caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName);
}
}
}
如上,Reflector 的构造方法看起来略为复杂,不过好在一些比较复杂的逻辑都封装在了相应的方法中,这样整体的逻辑就比较清晰了。
getter 方法解析过程
getter 方法解析的逻辑被封装在了addGetMethods方法中,这个方法除了会解析形如getXXX的方法,同时也会解析isXXX方法。该方法的源码分析如下:
private void addGetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>();
// 获取当前类,接口,以及父类中的方法。该方法逻辑不是很复杂,这里就不展开了
Method[] methods = getClassMethods(cls);
// getter 方法不应该有参数,若存在参数,则忽略当前方法
for (Method method : methods) {
if (method.getParameterTypes().length > 0) {
continue;
}
String name = method.getName();
// 过滤出以 get 或 is 开头的方法
if ((name.startsWith("get") && name.length() > 3)
|| (name.startsWith("is") && name.length() > 2)) {
// 将 getXXX 或 isXXX 等方法名转成相应的属性,比如 getName -> name
name = PropertyNamer.methodToProperty(name);
/*
* 将冲突的方法添加到 conflictingGetters 中。考虑这样一种情况:
*
* getTitle 和 isTitle 两个方法经过 methodToProperty 处理,
* 均得到 name = title,这会导致冲突。
*
* 对于冲突的方法,这里先统一起存起来,后续再解决冲突
*/
addMethodConflict(conflictingGetters, name, method);
}
}
// 解决 getter 冲突
resolveGetterConflicts(conflictingGetters);
}
如上,addGetMethods 方法的执行流程如下:
- 获取当前类,接口,以及父类中的方法
- 遍历上一步获取的方法数组,并过滤出以get和is开头的方法
- 将方法名转换成相应的属性名
- 将属性名和方法对象添加到冲突集合中
- 解决冲突
前四步比较简单,大家看一下就能懂。在这几步中,第5步逻辑比较复杂,这一步逻辑我们重点关注一下。
/** 添加属性名和方法对象到冲突集合中 */
private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) {
List<Method> list = conflictingMethods.get(name);
if (list == null) {
list = new ArrayList<Method>();
conflictingMethods.put(name, list);
}
list.add(method);
}
/** 解决冲突 */
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;
}
// 获取返回值类型
Class<?> winnerType = winner.getReturnType();
Class<?> candidateType = candidate.getReturnType();
/*
* 两个方法的返回值类型一致,若两个方法返回值类型均为 boolean,则选取 isXXX 方法
* 为 winner。否则无法决定哪个方法更为合适,只能抛出异常
*/
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.");
/*
* 如果方法返回值类型为 boolean,且方法名以 "is" 开头,
* 则认为候选方法 candidate 更为合适
*/
} else if (candidate.getName().startsWith("is")) {
winner = candidate;
}
/*
* winnerType 是 candidateType 的子类,类型上更为具体,
* 则认为当前的 winner 仍是合适的,无需做什么事情
*/
} else if (candidateType.isAssignableFrom(winnerType)) {
/*
* candidateType 是 winnerType 的子类,此时认为 candidate 方法更为合适,
* 故将 winner 更新为 candidate
*/
} 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.");
}
}
// 将筛选出的方法添加到 getMethods 中,并将方法返回值添加到 getTypes 中
addGetMethod(propName, winner);
}
}
private void addGetMethod(String name, Method method) {
if (isValidPropertyName(name)) {
getMethods.put(name, new MethodInvoker(method));
// 解析返回值类型
Type returnType = TypeParameterResolver.resolveReturnType(method, type);
// 将返回值类型由 Type 转为 Class,并将转换后的结果缓存到 setTypes 中
getTypes.put(name, typeToClass(returnType));
}
}
上就是解除冲突的过程,代码有点长,不太容易看懂。这里大家只要记住解决冲突的规则即可理解上面代码的逻辑。相关规则如下:
- 冲突方法的返回值类型具有继承关系,子类返回值对应的方法被认为是更合适的选择
- 冲突方法的返回值类型相同,如果返回值类型为boolean,那么以is开头的方法则是更合适的方法
- 冲突方法的返回值类型相同,但返回值类型非boolean,此时出现歧义,抛出异常
- 冲突方法的返回值类型不相关,无法确定哪个是更好的选择,此时直接抛异常
分析完 getter 方法的解析过程,下面继续分析 setter 方法的解析过程。
setter 方法解析过程
与 getter 方法解析过程相比,setter 方法的解析过程与此有一定的区别。主要体现在冲突出现的原因,以及冲突的解决方法上。那下面,我们深入源码来找出两者之间的区别。
private void addSetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>();
// 获取当前类,接口,以及父类中的方法。该方法逻辑不是很复杂,这里就不展开了
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
String name = method.getName();
// 过滤出 setter 方法,且方法仅有一个参数
if (name.startsWith("set") && name.length() > 3) {
if (method.getParameterTypes().length == 1) {
name = PropertyNamer.methodToProperty(name);
/*
* setter 方法发生冲突原因是:可能存在重载情况,比如:
* void setSex(int sex);
* void setSex(SexEnum sex);
*/
addMethodConflict(conflictingSetters, name, method);
}
}
}
// 解决 setter 冲突
resolveSetterConflicts(conflictingSetters);
}
从上面的代码和注释中,我们可知道 setter 方法之间出现冲突的原因。即方法存在重载,方法重载导致methodToProperty方法解析出的属性名完全一致。而 getter 方法之间出现冲突的原因是getXXX和isXXX对应的属性名一致。既然冲突发生了,要进行调停,那接下来继续来看看调停冲突的逻辑。
private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) {
for (String propName : conflictingSetters.keySet()) {
List<Method> setters = conflictingSetters.get(propName);
/*
* 获取 getter 方法的返回值类型,由于 getter 方法不存在重载的情况,
* 所以可以用它的返回值类型反推哪个 setter 的更为合适
*/
Class<?> getterType = getTypes.get(propName);
Method match = null;
ReflectionException exception = null;
for (Method setter : setters) {
// 获取参数类型
Class<?> paramType = setter.getParameterTypes()[0];
if (paramType.equals(getterType)) {
// 参数类型和返回类型一致,则认为是最好的选择,并结束循环
match = setter;
break;
}
if (exception == null) {
try {
// 选择一个更为合适的方法
match = pickBetterSetter(match, setter, propName);
} catch (ReflectionException e) {
match = null;
exception = e;
}
}
}
// 若 match 为空,表示没找到更为合适的方法,此时抛出异常
if (match == null) {
throw exception;
} else {
// 将筛选出的方法放入 setMethods 中,并将方法参数值添加到 setTypes 中
addSetMethod(propName, match);
}
}
}
/** 从两个 setter 方法中选择一个更为合适方法 */
private Method pickBetterSetter(Method setter1, Method setter2, String property) {
if (setter1 == null) {
return setter2;
}
Class<?> paramType1 = setter1.getParameterTypes()[0];
Class<?> paramType2 = setter2.getParameterTypes()[0];
// 如果参数2可赋值给参数1,即参数2是参数1的子类,则认为参数2对应的 setter 方法更为合适
if (paramType1.isAssignableFrom(paramType2)) {
return setter2;
// 这里和上面情况相反
} else if (paramType2.isAssignableFrom(paramType1)) {
return setter1;
}
// 两种参数类型不相关,这里抛出异常
throw new ReflectionException("Ambiguous setters defined for property '" + property + "' in class '"
+ setter2.getDeclaringClass() + "' with types '" + paramType1.getName() + "' and '"
+ paramType2.getName() + "'.");
}
private void addSetMethod(String name, Method method) {
if (isValidPropertyName(name)) {
setMethods.put(name, new MethodInvoker(method));
// 解析参数类型列表
Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type);
// 将参数类型由 Type 转为 Class,并将转换后的结果缓存到 setTypes
setTypes.put(name, typeToClass(paramTypes[0]));
}
}
关于 setter 方法冲突的解析规则,这里也总结一下吧。如下:
- 冲突方法的参数类型与 getter 的返回类型一致,则认为是最好的选择
- 冲突方法的参数类型与 getter 的返回类型一致,则认为是最好的选择
- 冲突方法的参数类型不相关,无法确定哪个是更好的选择,此时直接抛异常
到此关于 setter 方法的解析过程就说完了。我在前面说过 MetaClass 的hasSetter最终调用了 Refactor 的hasSetter方法,那么现在是时候分析 Refactor 的hasSetter方法了。代码如下如下:
public boolean hasSetter(String propertyName) {
return setMethods.keySet().contains(propertyName);
}
PropertyTokenizer 源码分析
对于较为复杂的属性,需要进行进一步解析才能使用。那什么样的属性是复杂属性呢? PropertyTokenizer 对数组和复合属性均进行了处理。那它是如何处理的呢?
public class PropertyTokenizer implements Iterator<PropertyTokenizer> {
private String name;
private final String indexedName;
private String index;
private final String children;
public PropertyTokenizer(String fullname) {
// 检测传入的参数中是否包含字符 '.'
int delim = fullname.indexOf('.');
if (delim > -1) {
/*
* 以点位为界,进行分割。比如:
* fullname = www.coolblog.xyz
*
* 以第一个点为分界符:
* name = www
* children = coolblog.xyz
*/
name = fullname.substring(0, delim);
children = fullname.substring(delim + 1);
} else {
// fullname 中不存在字符 '.'
name = fullname;
children = null;
}
indexedName = name;
// 检测传入的参数中是否包含字符 '['
delim = name.indexOf('[');
if (delim > -1) {
/*
* 获取中括号里的内容,比如:
* 1. 对于数组或List集合:[] 中的内容为数组下标,
* 比如 fullname = articles[1],index = 1
* 2. 对于Map:[] 中的内容为键,
* 比如 fullname = xxxMap[keyName],index = keyName
*
* 关于 index 属性的用法,可以参考 BaseWrapper 的 getCollectionValue 方法
*/
index = name.substring(delim + 1, name.length() - 1);
// 获取分解符前面的内容,比如 fullname = articles[1],name = articles
name = name.substring(0, delim);
}
}
// 省略 getter
@Override
public boolean hasNext() {
return children != null;
}
@Override
public PropertyTokenizer next() {
// 对 children 进行再次切分,用于解析多重复合属性
return new PropertyTokenizer(children);
}
// 省略部分方法
}