在使用 MyBatis 时,第一步要做的事情一般是根据配置文件构建 SqlSessionFactory对象。

1
2
3
4
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(inputStream);

们首先会使用 MyBatis 提供的工具类 Resources加载配置文件,得到一个输入流。然后再通过 SqlSessionFactoryBuilder 对象的 build 方法构建 SqlSessionFactory对象。这里的 build 方法是我们分析配置文件解析过程的入口方法。

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
// -☆- SqlSessionFactoryBuilder
public SqlSessionFactory build(InputStream inputStream) {
// 调用重载方法
return build(inputStream, null, null);
}

public SqlSessionFactory build(InputStream inputStream, String environment,
Properties properties) {
try {
// 创建配置文件解析器
XMLConfigBuilder parser =
new XMLConfigBuilder(inputStream, environment, properties);
// 调用 parse 方法解析配置文件,生成 Configuration 对象
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("……", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {……}
}
}

public SqlSessionFactory build(Configuration config) {
// 创建 DefaultSqlSessionFactory
return new DefaultSqlSessionFactory(config);
}

大致可以猜出 MyBatis 配置文件是通过 XMLConfigBuilder 进行解析的

parse 方法

1
2
3
4
5
6
7
8
9
10
// -☆- XMLConfigBuilder
public Configuration parse() {
if (parsed) {
throw new BuilderException("……");
}
parsed = true;
// 解析配置
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}

注意一个 xpath 表达式—— /configuration。这个表达式代表的是 MyBatis 配置文件的 节点,这里通过 xpath 选中这个节点,并传递给 parseConfiguration 方法。

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
private void parseConfiguration(XNode root) {
try {
// 解析 properties 配置
propertiesElement(root.evalNode("properties"));

// 解析 settings 配置,并将其转换为 Properties 对象
Properties settings =
settingsAsProperties(root.evalNode("settings"));

// 加载 vfs
loadCustomVfs(settings);

// 解析 typeAliases 配置
typeAliasesElement(root.evalNode("typeAliases"));

// 解析 plugins 配置
pluginElement(root.evalNode("plugins"));

// 解析 objectFactory 配置
objectFactoryElement(root.evalNode("objectFactory"));

// 解析 objectWrapperFactory 配置
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

// 解析 reflectorFactory 配置
reflectorFactoryElement(root.evalNode("reflectorFactory"));

// settings 中的信息设置到 Configuration 对象中
settingsElement(settings);

// 解析 environments 配置
environmentsElement(root.evalNode("environments"));

// 解析 databaseIdProvider,获取并设置 databaseId 到 Configuration 对象
databaseIdProviderElement(root.evalNode("databaseIdProvider"));

// 解析 typeHandlers 配置
typeHandlerElement(root.evalNode("typeHandlers"));

// 解析 mappers 配置
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("……");
}
}

到此,一个完整的配置解析过程就呈现出来了,每个节点的的解析逻辑均封装在了相应的方法中。我在分析这些方法时,并不会按照上面代码中所呈现的解析顺序进行分析,而是做了一定的调整。