相关配置是 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 方法等。
简单总结一下上面代码的逻辑:
- 解析 settings 子节点的内容,并将解析结果转成 Properties 对象
- Configuration 创建元信息对象
- 通过 MetaClass 检测Configuration中是否存在某个属性的setter方法,不存在则抛异常
- 若通过 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 方法。上边出现几个类:
- ReflectorFactory:顾名思义,Reflector 的工厂类,兼有缓存 Reflector 对象的功能
- Reflector:反射器,用于解析和存储目标类中的元信息
- 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 本身代码比较多,分析该类的部分逻辑
- Reflector 构造方法及成员变量分析
- getter 方法解析过程
- 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); }
|
代码不是很多,但是逻辑有点多
- 获取当前类,接口,以及父类中的方法
- 遍历上一步获取的方法数组,并过滤出以 get 和 is 开头的方法
- 将方法名转换成相应的属性名
- 将属性名和方法对象添加到冲突集合中
- 解决冲突
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)); } }
|
以上就是解除冲突的过程:
- 冲突方法返回值类型具有继承关系,子类返回值对应方法被认为是更合适的选择
- 冲突方法的返回值类型相同,如果返回值类型为boolean,那么以is开头的方法则是更合适的选择
- 冲突方法的返回值类型相同,但类型非 boolean,此时出现歧义,抛出异常
- 冲突方法的返回值类型不相关,无法确定哪个是更好的选择,此时直接抛异常
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 方法冲突的解析规则:
- 冲突方法的参数类型与 getter 的返回类型一致,则认为是最好的选择
- 冲突方法的参数类型具有继承关系,子类参数对应的方法被认为是更合适的选择
- 冲突方法的参数类型不相关,无法确定哪个是更好的选择,此时直接抛异常
前面说过 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); } }
|