映射文件的解析过程是配置文件解析过程的一部分,MyBatis 会在解析配置文件的过程中对映射文件进行解析。解析逻辑封装在 mapperElement 方法中

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
// -☆- XMLConfigBuilder
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 获取 <package> 节点中的 name 属性
String mapperPackage = child.getStringAttribute("name");
// 从指定包中查找 mapper 接口,并根据 mapper 接口解析映射配置
configuration.addMappers(mapperPackage);
} else {
// 获取 resource/url/class 等属性
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
// resource 不为空,且其他两者为空,则从指定路径中加载配置
if (resource != null && url == null && mapperClass == null){
ErrorContext.instance().resource(resource);
InputStream inputStream =
Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(
inputStream, configuration, resource,
configuration.getSqlFragments());
// 解析映射文件
mapperParser.parse();
// url 不为空,且其他两者为空,则通过 url 加载配置
} else if (resource == null &&
url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(
inputStream, configuration, url,
configuration.getSqlFragments());
// 解析映射文件
mapperParser.parse();
// mapperClass 不为空,且其他两者为空,
// 则通过 mapperClass 解析映射配置
} else if (resource == null &&
url == null && mapperClass != null) {
Class<?> mapperInterface =
Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
// 以上条件不满足,则抛出异常
} else {
throw new BuilderException("……");
}
}
}
}
}

主要逻辑是遍历mappers的子节点,并根据节点属性值判断通过何种方式加载映射文件或映射信息。这里把配置在注解中的内容称为映射信息,以XML为载体的配置称为映射文件。

在 MyBatis 中,共有四种加载映射文件或映射信息的方式:

  1. 从文件系统中加载映射文件
  2. 通过 URL 的方式加载映射文件
  3. 通过 mapper接口加载映射信息,映射信息可以配置在注解中,也可以配置在映射文件中
  4. 通过包扫描的方式获取到某个包下的所有类,并使用第三种方式为每个类解析映射信息

需要注意的是,在 MyBatis中,通过注解配置映射信息的方式是有一定局限性的,这一点 MyBatis 官方文档中描述的比较清楚。

因为最初设计时,MyBatis 是一个 XML 驱动的框架。配置信息是基于 XML 的,而且映射
语句也是定义在 XML 中的。而到了 MyBatis3,就有新选择了。MyBatis3 构建在全面且强
大的基于 Java 语言的配置 API 之上。这个配置 API 是基于 XML 的 MyBatis 配置的基础,也是新的基于注解配置的基础。注解提供了一种简单的方式来实现简单映射语句,而不会引入大量的开销

注意:不幸的是,Java 注解的表达力和灵活性十分有限。尽管很多时间都花在调查、设计和试验上,最强大的MyBatis映射并不能用注解来构建——并不是在开玩笑,的确是这样。

因此,对于一些较为复杂的配置信息,我们还是应该通过XML 的方式进行配置。

映射文件解析入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// -☆- XMLMapperBuilder
public void parse() {
// 检测映射文件是否已经被解析过
if (!configuration.isResourceLoaded(resource)) {
// 解析 mapper 节点
configurationElement(parser.evalNode("/mapper"));
// 添加资源路径到“已解析资源集合”中
configuration.addLoadedResource(resource);
// 通过命名空间绑定 Mapper 接口
bindMapperForNamespace();
}

// 处理未完成解析的节点
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}

映射文件解析入口逻辑包含三个核心操作,如下:

  1. 解析 mapper 节点
  2. 通过命名空间绑定 Mapper 接口
  3. 处理未完成解析的节点