相关配置是 MyBatis 中非常重要的配置,这些配置用于调整 MyBatis 运行时的行为。settings 配置繁多,在对这些配置不熟悉的情况下,保持默认配置即可。

比较简单的配置,如下:

1
2
3
4
5
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
</settings>

分析相关源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// -☆- XMLConfigBuilder
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
// 获取 settings 子节点中的内容
Properties props = context.getChildrenAsProperties();
// 创建 Configuration 类的“元信息”对象
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
// 检测 Configuration 中是否存在相关属性,不存在则抛出异常
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("……");
}
}
return props;
}

在上面的代码中出现了一个陌生的类MetaClass,这个类是用来做什么的呢?答案是用来解析目标类的一些元信息,比如类的成员变量,getter/setter 方法等。

简单总结一下上面代码的逻辑:

  1. 解析 settings 子节点的内容,并将解析结果转成 Properties 对象
  2. Configuration 创建元信息对象
  3. 通过 MetaClass 检测Configuration中是否存在某个属性的setter方法,不存在则抛异常
  4. 若通过 MetaClass 的检测,则返回 Properties 对象,方法逻辑结束

元信息对象创建过程

元信息类 MetaClass 的构造方法为私有类型,所以不能直接创建,必须使用其提供的forClass 方法进行创建。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class MetaClass {
private final ReflectorFactory reflectorFactory;
private final Reflector reflector;
private MetaClass(Class<?> type, ReflectorFactory reflectorFactory) {
this.reflectorFactory = reflectorFactory;
// 根据类型创建 Reflector
this.reflector = reflectorFactory.findForClass(type);
}
public static MetaClass forClass(
Class<?> type, ReflectorFactory reflectorFactory) {
// 调用构造方法
return new MetaClass(type, reflectorFactory);
}
// 省略其他方法
}

出现了两个新的类ReflectorFactory和Reflector,MetaClass通过引入这些新类帮助它完成功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// -☆- MetaClass
public boolean hasSetter(String name) {
// 属性分词器,用于解析属性名
PropertyTokenizer prop = new PropertyTokenizer(name);
// hasNext 返回 true,则表明 name 是一个复合属性,后面会进行分析
if (prop.hasNext()) {
// 调用 reflector 的 hasSetter 方法
if (reflector.hasSetter(prop.getName())) {
// 为属性创建创建 MetaClass
MetaClass metaProp = metaClassForProperty(prop.getName());
// 再次调用 hasSetter
return metaProp.hasSetter(prop.getChildren());
} else {
return false;
}
} else {
// 调用 reflector 的 hasSetter 方法
return reflector.hasSetter(prop.getName());
}
}

MetaClass 中的 hasSetter 方法最终调用了 Reflector 的hasSetter 方法。上边出现几个类:

  1. ReflectorFactory:顾名思义,Reflector 的工厂类,兼有缓存 Reflector 对象的功能
  2. Reflector:反射器,用于解析和存储目标类中的元信息
  3. PropertyTokenizer:属性名分词器,用于处理较为复杂的属性名

单独分析一下这几个类的逻辑,首先是 ReflectorFactory。

ReflectorFactory

目前只有一个实现类 DefaultReflectorFactory,用于创建Reflector,同时兼有缓存的功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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);
}
}
}

逻辑不是很复杂

Reflector

这个类的用途主要是是通过反射获取目标类的getter方法及其返回值类型,setter方法及其参数值类型等元信息。并将获取到的元信息缓存到相应的集合中,供后续使用。

Reflector 本身代码比较多,分析该类的部分逻辑

  1. Reflector 构造方法及成员变量分析
  2. getter 方法解析过程
  3. setter 方法解析过程

Reflector 构造⽅法及成员变量分析

Reflector 构造方法中包含了很多初始化逻辑,目标类的元信息解析过程也是在构造方法中完成的,这些元信息最终会被保存到 Reflector 的成员变量中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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 的构造方法逻辑比较多,看起来比较复杂。不过好在一些较为复杂的逻
辑都封装在了相应的方法中,这样整体的逻辑就比较清晰。

表格列举一下 Reflector 部分成员变量的用途。

变量 用途
readablePropertyNames 可读属性名称数组,用于保存 getter 方法对应的属性名称
writeablePropertyNames 可写属性名称数组,用于保存 setter 方法对应的属性名称
setMethods 用于保存属性名称到 Invoke 的映射。setter方法会被封装到 MethodInvoker对象中,Invoke 实现类比较简单,大家自行分析
getMethods 用于保存属性名称到 Invoke 的映射。同上,getter方法也会被封装到 MethodInvoker 对象中
setTypes 用于保存 setter 对应的属性名与参数类型的映射
getTypes 用于保存 getter 对应的属性名与返回值类型的映射
caseInsensitivePropertyMap 用于保存大写属性名与属性名之间的映射,比如 <NAME, name>

这些变量用于缓存各种原信息。关于这些变量,这里描述的不太好懂,主要是这些变量用途不太好解释。

getter ⽅法解析过程

getter 方法解析的逻辑被封装在了 addGetMethods 方法中。这个方法除了会解析形如
getXXX 的方法,同时也会解析 isXXX 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private void addGetMethods(Class<?> cls) {
Map<String, List<Method>> conflictingGetters =
new HashMap<String, List<Method>>();

// 获取当前类,接口,以及父类中的方法。该方法逻辑不是很复杂,这里就不展开了
Method[] methods = getClassMethods(cls);
for (Method method : methods) {
// getter 方法不应该有参数,若存在参数,则忽略当前方法
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);
}

代码不是很多,但是逻辑有点多

  1. 获取当前类,接口,以及父类中的方法
  2. 遍历上一步获取的方法数组,并过滤出以 get 和 is 开头的方法
  3. 将方法名转换成相应的属性名
  4. 将属性名和方法对象添加到冲突集合中
  5. 解决冲突
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/** 添加属性名和方法对象到冲突集合中 */
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("……");
/*
* 如果方法返回值类型为 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("……");
}
}
// 将筛选出的方法添加到 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));
}
}

以上就是解除冲突的过程:

  1. 冲突方法返回值类型具有继承关系,子类返回值对应方法被认为是更合适的选择
  2. 冲突方法的返回值类型相同,如果返回值类型为boolean,那么以is开头的方法则是更合适的选择
  3. 冲突方法的返回值类型相同,但类型非 boolean,此时出现歧义,抛出异常
  4. 冲突方法的返回值类型不相关,无法确定哪个是更好的选择,此时直接抛异常

setter ⽅法解析过程

与 getter 方法解析过程相比,setter方法的解析过程与此有一定的区别。主要体现在冲突出现的原因,以及冲突的解决方法上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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);
}

出现冲突的原因:方法存在重载,方法重载导致methodToProperty方法解析出的属性名完全一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
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("……");
}
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 方法冲突的解析规则:

  1. 冲突方法的参数类型与 getter 的返回类型一致,则认为是最好的选择
  2. 冲突方法的参数类型具有继承关系,子类参数对应的方法被认为是更合适的选择
  3. 冲突方法的参数类型不相关,无法确定哪个是更好的选择,此时直接抛异常

前面说过 MetaClass 的 hasSetter 最终调用了 Refactor 的 hasSetter 方法

1
2
3
public boolean hasSetter(String propertyName) {
return setMethods.keySet().contains(propertyName);
}

PropertyTokenizer

对于较为复杂的属性,需要进行进一步解析才能使用。那什么样的属性是复杂属性呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class MetaClassTest {
private class Author {
private Integer id;
private String name;
private Integer age;
/** 一个作者对应多篇文章 */
private Article[] articles;
// 省略 getter/setter
}

private class Article {
private Integer id;
private String title;
private String content;
/** 一篇文章对应一个作者 */
private Author author;
// 省略 getter/setter
}
@Test
public void testHasSetter() {
// 为 Author 创建元信息对象
MetaClass authorMeta = MetaClass.forClass(
Author.class, new DefaultReflectorFactory());
System.out.println("------------☆ Author ☆------------");
System.out.println("id -> " + authorMeta.hasSetter("id"));
System.out.println("name -> " + authorMeta.hasSetter("name"));
System.out.println("age -> " + authorMeta.hasSetter("age"));
// 检测 Author 中是否包含 Article[] 的 setter
System.out.println("articles->" + authorMeta.hasSetter("articles"));
System.out.println("articles[] -> " +
authorMeta.hasSetter("articles[]"));
System.out.println("title -> " + authorMeta.hasSetter("title"));

// 为 Article 创建元信息对象
MetaClass articleMeta = MetaClass.forClass(
Article.class, new DefaultReflectorFactory());
System.out.println("\n------------☆ Article ☆------------");
System.out.println("id -> " + articleMeta.hasSetter("id"));
System.out.println("title -> " + articleMeta.hasSetter("title"));
System.out.println("content -> " +
articleMeta.hasSetter("content"));
// 下面两个均为复杂属性,分别检测 Article 类中的 Author 类
// 是否包含 id 和 name 的 setter 方法
System.out.println("author.id->"+
articleMeta.hasSetter("author.id"));
System.out.println("author.name->" +
articleMeta.hasSetter("author.name"));
}
}

Article 类中包含了一个 Author 引用。然后我们调用 articleMeta 的 hasSetter 检测
author.id 和 author.name 属性是否存在,我们的期望结果为 true。

这说明 PropertyTokenizer对数组和复合属性均进行了处理。那它是如何处理的呢?答案如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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 = 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);
}
}