MyBatis基础支持层位于 Mybatis 整体架构的最底层,支撑着 Mybatis 的核心处理层,是整个框架的基石。基础支持层中封装了多个较为通用的、独立的模块,不仅仅为 Mybatis 提供基础支撑,也可以在合适的场景中直接复用。

MyBatis基础支持层
MyBatis基础支持层

解析器模块

Mybatis中涉及大量的XML配置文件,常见的XML解析方式:DOM、SAX和StAX。

Mybatis 在初始化过程中处理 mybatis-config xml 配置文件以及映射文件时,使用的是 DOM 解析方式,并结合使用 Xpath 解析 XML 配置文件。正如前文所述,DOM 会将整个 XML 文档加载到内存中并形成树状数据结构,而 XPah 是一种为查询 XML 文档而设计的语言,它可以与 DOM 解析方式配合使用,实现对 XML 文档的解析。

Xpath 之于 XML 就好比 SQL 语言之于数据库。

XPathParser

Mybatis 提供的 Xpathparser 类封装了XpathDocumentEntityresolver

XPathParser
XPathParser

Xpathparser 中各个字段的含义和功能如下所示。

1
2
3
4
5
6
private Document document; // Document 对象 private boolean validation; //是否开启验证

private Entityresolver entityresolver; //用于加载本地 DTD 文件

private Properties variables; // mybatis- config. Xm1 中《propteries》标签定义的键值对集合
private Xpath xpath; // Xpath 对象

默认情况下,对 XML 文档进行验证时,会根据 XML 文档开始位置指定的网址加载对应的 DTD 文件或 XSD 文件。如果解析 mybatis- config. Xml 配置文件,默认联网加载 htp: / mybatis. Org, / dtd/mybatis-3- config. Dtd 这个 DTD 文档,当网络比较慢时会导致验证过程缓慢。在实践中往往会提前设置 Entityresolver 接口对象加载本地的 DTD 文件,从而避免联网加载 DTD 文件。Xmlmapperentity Resolver 是 Mybatis 提供的 Entity Resolver 接口的实现类。

EntityResolver
EntityResolver
  • Xpathparser 中提供了一系列的 eval*0 方法用于解析 boolean、shot、long、int、String、Node 等类型的信息,它通过调用前面介绍的 Xpath. Evaluate 方法查找指定路径的节点或属性,并进行相应的类型装换。具体代码比较简单,就不贴出来了。这里需要注意的是 Xpathparser. Evalstringo 方法,其中会调用 Propertyparser. Parse 方法处理节点中相应的默认值。

  • 在 Propertyparser 中指定了是否开启使用默认值的功能以及默认的分隔符

  • Property Parser parse 方法中会创建 Generic Tokenparser 解析器,并将默认值的处理委托给Generic Tokenparser parse()方法。

  • Generic Tokenparser 是一个通用的字占位符解析器,其字段的含义如下:

    1
    2
    3
    4
    Private final String opentoken; //占位符的开始标记 
    private final String closetoken; //占位符的结東标记

    private final Tokenhandler handler; // Tokenhandler 接口的实现会按照一定的逻辑解析占位符

    Generic Tokenparser parse 方法的逻辑并不复杂,它会顺序查找 open Token 和 close Token,解析得到占位符的字面值,并将其交给 Tokenhandler 处理,然后将解析结果重新拼装成字符串并返回。

反射模块

反射模块
反射模块

反射工具箱

Mybatis 在进行参数处理、结果映射等操作时,会涉及大量的反射操作。Java 中的反射虽然功能强大,但是代码编写起来比较复杂且容易出错,为了简化反射操作的相关代码,Mybatis 提供了专门的反射模块,该模块位于 org.apache.ibatis.reflection 包中,它对常见的反射操作做了进一步封装,提供了更加简洁方便的反射 API。

Reflector & ReflectorFactory

Reflector是MyBatis中反射模块的基础,每隔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
Private Class <?> type; //对应的 C1 ass 类型

//可读属性的名称集合,可读属性就是存在相应 getter 方法的属性,初始值为空数组
private String I readablepropertynames= EMPTY STRING ARRAY

//可写属性的名称集合,可写属性就是存在相应 setter 方法的属性,初始值为空数组
private String [writeablepropertynames EMPTY STRING ARRAY;
//记录了属性相应的 setter 方法,key 是属性名称,value 是 Invoker 对象,它是对 setter 方法对应
// Me thod 对象的封装,后面会详细介绍
private Map <String, Invoker> setmethods =new Hashmap <String, Invoker> ();

//属性相应的 getter 方法集合,key 是属性名称,value 也是 Invoker 对象
private Map <String, Invoker> getmethods =new Hashmap <String, Invoker> ();

//记录了属性相应的 setter 方法的参数值类型,key 是属性名称,value 是 setter 方法的参数类型
private Map <String, Class <?>> settypes =new Hashmap <string, Class <?>> ();

//记录了属性相应的 getter 方法的返回值类型,key 是属性名称,value 是 getter 方法的返回值类型
private Map <string, Class <?>> gettypes=new Hashmap <string, Class <?>> ();

private Constructor <?> default Constructor; //记录了默认构造方法

//记录了所有属性名称的集合
private Map <string, String> caseinsensitivepropertymap-new Hashmap <String, String> ();

在Reflector的构造方法中会解析制定的Class对象,并填充上述集合。具体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public Reflector(Class<?> clazz) {
type = clazz; // 初始化type字段
// 查找clazz的默认构造方法,具体实现是通过反射遍历所有构造方法
addDefaultConstructor(clazz);
addGetMethods(clazz); // 处理clazz中的getter方法,填充getMethods集合和getTypes集合
addSetMethods(clazz); // 处理clazz中的setter方法,填充setMethods集合和setTypes集合
addFields(clazz); // 处理没有getter/setter方法的字段

// 根据getMethods/setMethods集合,初始化可读/写属性的名称集合
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.Addgetmethodso()方法主要负责解析类中定义的 getter 方法,Reflector. addSetMethods()方法负责解析类中定义的 setter 方法,两者的逻辑类似。

Reflector. Addgetmethods() 方法有如下三个核心步骤。

  1. 首先,调用Reflector.getClassMethods()方法获取当前类及其父类中定义的所有方法的唯一签名以及响应的Method对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    private Method[] getClassMethods(Class<?> clazz) {
    // 用于记录制定类中定义的全部方法的唯一签名以及对应的Method对象
    Map<String, Method> uniqueMethods = new HashMap<>();
    Class<?> currentClass = clazz;
    while (currentClass != null && currentClass != Object.class) {

    // 记录currentClass这个类中定义的全部方法
    addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods());

    // we also need to look for interface methods -
    // because the class may be abstract
    // 记录接口中定义的方法
    Class<?>[] interfaces = currentClass.getInterfaces();
    for (Class<?> anInterface : interfaces) {
    addUniqueMethods(uniqueMethods, anInterface.getMethods());
    }
    // 获取父类,继续while循环
    currentClass = currentClass.getSuperclass();
    }

    Collection<Method> methods = uniqueMethods.values();

    return methods.toArray(new Method[0]);// 转换成Methods数组返回
    }
  2. 然后,按照JavaBean的规范,从Reflector.getClassMethods()方法返回的Method数组中查找该类中定义的getter方法,将其记录到conflictingGetters集合中。conflictingGetters集合的key为属性名称,value是该属性对应的getter方法的集合。

  3. 当子类覆盖了父类的getter方法且返回值发生变化时,在步骤1中就会产生两个签名不同的方法。


未完待续。