Mybatis初始化加载过程(一)
案例
先看下一次完成的Mybatis查询过程示例代码:
1 | String resource = "mybatis-config.xml"; |
本文先重点分析,以下代码背后的原理
1 | SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); |
SqlSessionFactory创建
MyBatis初始化基本过程:
SqlSessionFactoryBuilder根据传入的数据流生成Configuration对象,然后根据Configuration对象创建默认的SqlSessionFactory实例。
MyBatis通过加载XML配置文件,将配置文信息组装成内部的Configuration对象,Configuration对象的组织结构和XML配置文件的组织结构几乎完全。
由此SqlSessionFactory的创建,有以下几个步骤:
- 调用SqlSessionFactoryBuilder对象的build(inputStream)方法;
- SqlSessionFactoryBuilder会根据输入流inputStream等信息创建XMLConfigBuilder对象;
- SqlSessionFactoryBuilder调用XMLConfigBuilder对象的parse()方法获取Configuration对象;
- SqlSessionFactoryBuilder根据Configuration对象创建一个DefaultSqlSessionFactory对象;
- SqlSessionFactoryBuilder返回 DefaultSqlSessionFactory对象。
SqlSessionFactoryBuilder相关的代码如下所示:
1 | public SqlSessionFactory build(InputStream inputStream) |
上述的初始化过程中,涉及到了以下几个对象:
- SqlSessionFactoryBuilder : SqlSessionFactory的构造器,用于创建SqlSessionFactory,采用了Builder设计模式
- Configuration :该对象是mybatis-config.xml文件中所有mybatis配置信息
- SqlSessionFactory:SqlSession工厂类,以工厂形式创建SqlSession对象,采用了Factory工厂设计模式
生成Configuration对象过程
XMLConfigBuilder会将XML配置文件的信息转换为Document对象,而XML配置定义文件DTD转换成XMLMapperEntityResolver对象,然后将二者封装到XpathParser对象中,XpathParser的作用是提供根据Xpath表达式获取基本的DOM节点Node信息的操作。如下图所示:
之后XMLConfigBuilder调用parse()方法:会从XPathParser中取出
节点对应的Node对象,然后解析此Node节点的子Node:properties, settings, typeAliases,typeHandlers, objectFactory, objectWrapperFactory, plugins, environments,databaseIdProvider, mappers 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
43public Configuration parse()
{
if (parsed)
{
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//源码中没有这一句,只有 parseConfiguration(parser.evalNode("/configuration"));
//为了让读者看得更明晰,源码拆分为以下两句
XNode configurationNode = parser.evalNode("/configuration");
parseConfiguration(configurationNode);
return configuration;
}
/*
解析 "/configuration"节点下的子节点信息,然后将解析的结果设置到Configuration对象中
*/
private void parseConfiguration(XNode root) {
try {
//1.首先处理properties 节点
propertiesElement(root.evalNode("properties")); //issue #117 read properties first
//2.处理typeAliases
typeAliasesElement(root.evalNode("typeAliases"));
//3.处理插件
pluginElement(root.evalNode("plugins"));
//4.处理objectFactory
objectFactoryElement(root.evalNode("objectFactory"));
//5.objectWrapperFactory
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//6.settings
settingsElement(root.evalNode("settings"));
//7.处理environments
environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
//8.database
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//9. typeHandlers
typeHandlerElement(root.evalNode("typeHandlers"));
//10 mappers
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}在上述代码中,还有一个非常重要的地方,就是解析XML配置文件子节点
的方法mapperElements(root.evalNode(“mappers”)), 它将解析我们配置的Mapper.xml配置文件。 然后将这些值解析出来设置到Configuration对象中。
创建SqlSession对象
在DefaultSqlSessionFactory中openSessionFromDataSource()方法返回DefaultSqlSession对象。
1 | /** |
设计模式应用
在创建SqlSessionFactory和Environment对象时用到了builder设计模式。
- 由于构造时参数不定,可以为其创建一个构造器Builder,将SqlSessionFactory的构建过程和表示分开
- 数据库连接环境Environment对象的创建
1
2
3
4
5
6
7
8
9
10
11
12
13
14private void environmentsElement(XNode context) throws Exception {
if (context != null) {
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
//使用了Environment内置的构造器Builder,传递id 事务工厂和数据源
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
}
}
}在下文我们将分析在获得sqlSession对象之后是怎样获取Mapper对象的,并执行SQL语句的(没有Mapper接口实现类的情况)。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
63public final class Environment {
private final String id;
private final TransactionFactory transactionFactory;
private final DataSource dataSource;
public Environment(String id, TransactionFactory transactionFactory, DataSource dataSource) {
if (id == null) {
throw new IllegalArgumentException("Parameter 'id' must not be null");
}
if (transactionFactory == null) {
throw new IllegalArgumentException("Parameter 'transactionFactory' must not be null");
}
this.id = id;
if (dataSource == null) {
throw new IllegalArgumentException("Parameter 'dataSource' must not be null");
}
this.transactionFactory = transactionFactory;
this.dataSource = dataSource;
}
public static class Builder {
private String id;
private TransactionFactory transactionFactory;
private DataSource dataSource;
public Builder(String id) {
this.id = id;
}
public Builder transactionFactory(TransactionFactory transactionFactory) {
this.transactionFactory = transactionFactory;
return this;
}
public Builder dataSource(DataSource dataSource) {
this.dataSource = dataSource;
return this;
}
public String id() {
return this.id;
}
public Environment build() {
return new Environment(this.id, this.transactionFactory, this.dataSource);
}
}
public String getId() {
return this.id;
}
public TransactionFactory getTransactionFactory() {
return this.transactionFactory;
}
public DataSource getDataSource() {
return this.dataSource;
}
}