本文,我们来分享 MyBatis 的反射模块,对应 reflection 包。如下图所示:
parsing 包来说,reflection 包的代码量大概是 2-3 倍。当然,不要慌,都是比较简单的代码。在 《精尽 MyBatis 源码解析 —— 项目结构一览》 中,简单介绍了这个模块如下:
Java 中的反射虽然功能强大,但对大多数开发人员来说,写出高质量的反射代码还是 有一定难度的。MyBatis 中专门提供了反射模块,该模块对 Java 原生的反射进行了良好的封装,提了更加简洁易用的 API,方便上层使调用,并且对反射操作进行了一系列优化,例如缓存了类的元数据,提高了反射操作的性能。
下面,我们就来看看具体的源码。因为 reflection 是基础支持层,所以建议胖友在我们讲解到的类和方法中,打折断点一起来了解。
org.apache.ibatis.reflection.Reflector ,反射器,每个 Reflector 对应一个类。Reflector 会缓存反射操作需要的类的信息,例如:构造方法、属性名、setting / getting 方法等等。代码如下:
// Reflector.java
|
type 属性,每个 Reflector 对应的类。defaultConstructor 属性,默认无参构造方法。在 <1> 处初始化,详细解析,见 「2.1 addDefaultConstructor」 。getMethods、getTypes 属性,分别为属性对应的 getting 方法、getting 方法的返回类型的映射。在 <2> 处初始化,详细解析,见 「2.2 addGetMethods」 。setMethods、setTypes 属性,分别为属性对应的 setting 方法、setting 方法的参数类型的映射。在 <3> 处初始化,详细解析,见 「2.3 addSetMethods」 。<4> 处,初始化 getMethods + getTypes 和 setMethods + setTypes ,通过遍历 fields 属性。详细解析,见 「2.4 addFields」 。<5> 处,初始化 readablePropertyNames、writeablePropertyNames、caseInsensitivePropertyMap 属性。#addDefaultConstructor(Class<?> clazz) 方法,查找默认无参构造方法。代码如下:
// Reflector.java
|
#addGetMethods(Class<?> cls) 方法,初始化 getMethods 和 getTypes ,通过遍历 getting 方法。代码如下:
// Reflector.java
|
<1> 处,conflictingGetters 变量,属性与其 getting 方法的映射。因为父类和子类都可能定义了相同属性的 getting 方法,所以 VALUE 会是个数组。<2> 处,调用 #getClassMethods(Class<?> cls) 方法,获得所有方法。详细解析,见 「2.2.1 getClassMethods」 。<3> 处,遍历所有方法,挑选符合的 getting 方法,添加到 conflictingGetters 中。
<3.1> 处,方法参数大于 0 ,说明不是 getting 方法,忽略。
<3.2> 处,方法名以 get 和 is 方法名开头,说明是 getting 方法。
<3.3> 处,调用 PropertyNamer#methodToProperty(String name) 方法,获得属性名。详细解析,见 「6.3 PropertyNamer」 。
<3.4> 处,调用 #addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) 方法,添加到 conflictingGetters 中。代码如下:
// Reflector.java
|
<4> 处,调用 #resolveGetterConflicts(Map<String, List<Method>>) 方法,解决 getting 冲突方法。详细解析,见 「2.2.2 resolveGetterConflicts」 。#getClassMethods(Class<?> cls) 方法,获得所有方法。代码如下:
// Reflector.java
|
代码比较简单,胖友自己看注释。
<1> 和 <2> 处,会调用 #addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) 方法,添加方法数组到 uniqueMethods 中。代码如下:
// Reflector.java
|
<3> 处,会调用 #getSignature(Method method) 方法,获得方法签名。代码如下:
// Reflector.java
|
returnType#方法名:参数名1,参数名2,参数名3 。void#checkPackageAccess:java.lang.ClassLoader,boolean 。#resolveGetterConflicts(Map<String, List<Method>>) 方法,解决 getting 冲突方法。最终,一个属性,只保留一个对应的方法。代码如下:
// Reflector.java
|
总体比较简单,胖友自己瞅瞅。我们只说两个关键点哈。
<1> 处,基于返回类型比较。重点在 <1.1> 和 <1.2> 的情况,因为子类可以修改放大返回值,所以在出现这个情况时,选择子类的该方法。例如,父类的一个方法的返回值为 List ,子类对该方法的返回值可以覆写为 ArrayList 。代码如下:
public class A {
|
<2> 处,调用 #addGetMethod(String name, Method method) 方法,添加方法到 getMethods 和 getTypes 中。代码如下:
// Reflector.java
|
<2.1> 处,调用 #isValidPropertyName(String name) 方法,判断是合理的属性名。代码如下:
// Reflector.java
|
<2.2> 处,添加到 getMethods 中。此处,我们可以看到一个 MethodInvoker 类,详细解析,见 「4.3 MethodInvoker」 。
<2.3> 处,添加到 getTypes 中。
此处,我们可以看到一个 TypeParameterResolver 类,详细解析,见 「14. TypeParameterResolver」 。
#typeToClass(Type src) 方法,获得 java.lang.reflect.Type 对应的类。代码如下:
// Reflector.java
|
#addSetMethods(Class<?> cls) 方法,初始化 setMethods 和 setTypes ,通过遍历 setting 方法。代码如下:
// Reflector.java
|
#addGetMethods(Class<?> cls) 方法差不多。主要差异点在 <1> 和 <2> 处。因为 <1> 一眼就能明白,所以我们只看 <2> ,调用 #resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) 方法,解决 setting 冲突方法。详细解析,见 「2.3.1 resolveSetterConflicts」 中。#resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) 方法,解决 setting 冲突方法。代码如下:
// Reflector.java
|
总体比较简单,胖友自己瞅瞅。我们只说两个关键点哈。
<1> 处,解决冲突 setting 方法的方式,实际和 getting 方法的方式是不太一样的。首先,多的就是考虑了对应的 getterType 为优先级最高。其次,#pickBetterSetter(Method setter1, Method setter2, String property) 方法,选择一个更加匹配的,和 getting 方法是相同的,因为要选择精准的方法。代码如下:
// Reflector.java
|
<2> 处,调用 #addSetMethod(String name, Method method) 方法,添加到 setMethods 和 setTypes 中。代码如下:
// Reflector.java
|
#addGetMethod(String name, Method method) 方法是类似的。#addFields(Class<?> clazz) 方法,初始化 getMethods + getTypes 和 setMethods + setTypes ,通过遍历 fields 属性。实际上,它是 #addGetMethods(...) 和 #addSetMethods(...) 方法的补充,因为有些 field ,不存在对应的 setting 或 getting 方法,所以直接使用对应的 field ,而不是方法。代码如下:
// Reflector.java
|
<1> 处,若 setMethods 不存在,则调用 #addSetField(Field field) 方法,添加到 setMethods 和 setTypes 中。代码如下:
// Reflector.java
|
<2> 处,若 getMethods 不存在,则调用 #addGetField(Field field) 方法,添加到 getMethods 和 getTypes 中。代码如下:
// Reflector.java
|
Reflector 中,还有其它方法,用于对它的属性进行访问。比较简单,感兴趣的胖友,自己来瞅瞅。例如:
// Reflector.java
|
org.apache.ibatis.reflection.ReflectorFactory ,Reflector 工厂接口,用于创建和缓存 Reflector 对象。代码如下:
// ReflectorFactory.java
|
org.apache.ibatis.reflection.DefaultReflectorFactory ,实现 ReflectorFactory 接口,默认的 ReflectorFactory 实现类。代码如下:
// DefaultReflectorFactory.java
|
org.apache.ibatis.reflection.invoker.Invoker ,调用者接口。代码如下:
// Invoker.java
|
#invoke(Object target, Object[] args) 方法,执行一次调用。而具体调用什么方法,由子类来实现。org.apache.ibatis.reflection.invoker.GetFieldInvoker ,实现 Invoker 接口,获得 Field 调用者。代码如下:
// GetFieldInvoker.java
|
org.apache.ibatis.reflection.invoker.SetFieldInvoker ,实现 Invoker 接口,设置 Field 调用者。代码如下:
// SetFieldInvoker.java
|
org.apache.ibatis.reflection.invoker.MethodInvoker ,实现 Invoker 接口,指定方法的调用器。代码如下:
// MethodInvoker.java
|
org.apache.ibatis.reflection.factory.ObjectFactory ,Object 工厂接口,用于创建指定类的对象。代码如下:
// ObjectFactory.java
|
org.apache.ibatis.reflection.factory.DefaultObjectFactory ,实现 ObjectFactory、Serializable 接口,默认 ObjectFactory 实现类。
#create(Class<T> type, ...) 方法,创建指定类的对象。代码如下:
// DefaultObjectFactory.java
|
<1> 处,调用 #resolveInterface(Class<?> type) 方法,获得需要创建的类。代码如下:
// DefaultObjectFactory.java
|
<1> 处,调用 #instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) 方法,创建指定类的对象。代码如下:
// DefaultObjectFactory.java
|
<x1>、<x2> 两种情况。#isCollection(Class<T> type) 方法,判断指定类是否为集合类。代码如下:
// DefaultObjectFactory.java
|
java.util.Collection 的子类。#setProperties(Properties properties) 方法,设置 Properties 。代码如下:
// DefaultObjectFactory.java
|
org.apache.ibatis.reflection.property 包下,提供了 PropertyCopier、PropertyNamer、PropertyTokenizer 三个属性相关的工具类。接下来,我们逐小节来解析。
org.apache.ibatis.reflection.property.PropertyCopier ,属性复制器。代码如下:
// PropertyNamer.java
|
org.apache.ibatis.reflection.property.PropertyNamer ,属性名相关的工具类方法。代码如下:
public final class PropertyNamer {
|
org.apache.ibatis.reflection.property.PropertyTokenizer ,实现 Iterator 接口,属性分词器,支持迭代器的访问方式。
举个例子,在访问 "order[0].item[0].name" 时,我们希望拆分成 "order[0]"、"item[0]"、"name" 三段,那么就可以通过 PropertyTokenizer 来实现。
// PropertyTokenizer.java
|
name 属性,当前字符串。children 属性,剩余字符串。<1> 处,初始化 name、children 字符串,使用 ‘.‘ 作为分隔。indexedName 属性,索引的 name 属性,因为 name 如果存在 index 会被更改。<2> 处,记录当前 name 。index 属性,编号。分成两种情况:
name 为数组 item[0] 时,则 index 为 "0" 。name 为 Map map[key] 时,则 index 为 "key" 。<3> 处,初始化 index ,并修改 name 字符串,使用 ‘[‘ 作为分隔符。#next() 方法,迭代获得下一个 PropertyTokenizer 对象。代码如下:
// PropertyTokenizer.java
|
#hasNext() 方法,判断是否有下一个元素。代码如下:
// PropertyTokenizer.java
|
PropertyTokenizer 中,还有其它方法,比较简单,感兴趣的胖友,自己来瞅瞅。
org.apache.ibatis.reflection.MetaClass ,类的元数据,基于 Reflector 和 PropertyTokenizer ,提供对指定类的各种骚操作。
// MetaClass.java
|
目前有两个方法会涉及到调用该构造方法:
① #forClass(Class<?> type, ReflectorFactory reflectorFactory) 静态方法,创建指定类的 MetaClass 对象。代码如下:
// MetaClass.java
|
② #metaClassForProperty(String name) 方法,创建类的指定属性的类的 MetaClass 对象。代码如下:
// MetaClass.java
|
#findProperty(String name, boolean useCamelCaseMapping) 方法,根据表达式,获得属性。代码如下:
// MetaClass.java
|
useCamelCaseMapping 属性,是否要下划线转驼峰 。但是,在 <1> 处,我们仅仅看到 _ 被替换成了空串。这是为什么呢?继续往下看。
<2> 处,调用 #findProperty(String name) 方法,根据表达式,获得属性。代码如下:
// MetaClass.java
|
<3> 处,调用 #buildProperty(String name, StringBuilder builder) 方法,构建属性。代码如下:
// MetaClass.java
|
* 创建 PropertyTokenizer 对象,对 `name` 进行**分词**。当有子表达式,继续递归调用 `#buildProperty(String name, StringBuilder builder)` 方法,并将结果添加到 `builder` 中;否则,结束,直接添加到 `builder` 中。 * 在两个 `<4>` 处,解决“下划线转驼峰”的关键是,通过 `Reflector.caseInsensitivePropertyMap` 属性,忽略大小写。代码如下:
// Reflector.java
|
* x
如果胖友,你有点懵逼,可以运行下 MetaClassTest#shouldFindPropertyName() 这个单元测试方法。
#hasGetter(String name) 方法,判断指定属性是否有 getting 方法。代码如下:
// MetaClass.java
|
思路和 #findProperty((String name, ...) 方法是一样的,所以胖友自己看下。
<1> 处,调用 #metaClassForProperty(PropertyTokenizer prop) 方法,创建 创建 MetaClass 对象。代码如下:
// MetaClass.java
|
metaClassForProperty => getGetterType => getGenericGetterType 。另外,#hasSetter(String name) 方法,判断指定属性是否有 setting 方法。逻辑上,和 #hasGetter(String name) 方法类似,胖友可以自己瞅瞅。
#getGetterType(String name) 方法,获得指定属性的 getting 方法的返回值的类型。代码如下:
// MetaClass.java
|
#hasGetter(String name) 方法类似,胖友可以自己瞅瞅。另外,#getSetterType(String name) 方法,判断指定属性是否有 setting 方法。逻辑上,和 #getGetterType(String name) 方法类似,胖友可以自己瞅瞅。
MetaClass 还有其它方法,比较简单,是基于 Reflector 方法的封装,感兴趣的胖友,可以自己看看。
org.apache.ibatis.reflection.wrapper.ObjectWrapper ,对象包装器接口,基于 MetaClass 工具类,定义对指定对象的各种操作。或者可以说,ObjectWrapper 是 MetaClass 的指定类的具象化。代码如下:
// ObjectWrapper.java
|
ObjectWrapper 的子类实现如下图:
org.apache.ibatis.reflection.wrapper.BaseWrapper ,实现 ObjectWrapper 接口,ObjectWrapper 抽象类,为子类 BeanWrapper 和 MapWrapper 提供属性值的获取和设置的公用方法。代码如下:
// BaseWrapper.java |