抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

MYBATIS初始化流程

加载配置文件关键类

  • BaseBuilder

    ​ 所有解析器的父类,包含配置文件实例,为解析文件提供的一些通用的方

  • XMLConfigBuilder

    ​ 主要负责解析mybatis-config.xml;

  • XMLMapperBuilder

    主要负责解析映射配置文件;

  • XMLStatementBuilder

    主要负责解析映射配置文件中的SQL节点;

XMLConfigBuilder、 XMLMapperBuilder、 XMLStatementBuilder 这三个类在配置文件加载
过程中非常重要,具体分工如下图所示:

映射器关键类

  • Configuration

    ​ Mybatis启动初始化的核心就是将所有xml配置文件信息加载到Configuration对象中, Configuration是单例的,生命周期是应用级的;

  • MapperRegistry

    ​ mapper接口动态代理工厂类的注册中心。在MyBatis中,通过mapperProxy实现InvocationHandler接口,MapperProxyFactory用于生成动态代理的实例对象;

  • ResultMap

    ​ 用于解析mapper.xml文件中的resultMap节点,使用ResultMapping来封装id,result等子元素;

  • MappedStatement

    ​ 用于存储mapper.xml文件中的select、insert、update和delete节点,同时还包含了这些节点的很多重要属性;

  • SqlSource

    ​ mapper.xml文件中的sql语句会被解析成SqlSource对象,经过解析SqlSource包含的语句最终仅仅包含?占位符,可以直接提交给数据库执行;

Configuration 对象

实例化并初始化 Configuration 对象是第一个阶段的最终目的,所以熟悉 configuration 对
象是理解第一个阶段代码的核心; configuration 对象的关键属性解析如下

  • MapperRegistry

    ​ mapper 接口动态代理工厂类的注册中心。在 MyBatis 中,通过mapperProxy 实现 InvocationHandler 接口, MapperProxyFactory 用于生成动态代理的实例对象

  • ResultMap

​ 用于解析 mapper.xml 文件中的 resultMap 节点,使用 ResultMapping 来封装id, result 等子元素;

  • MappedStatement

    ​ 用于存储 mapper.xml 文件中的 select、insert、update 和 delete 节点,同时还包含了这些节点的很多重要属性;

  • SqlSource

​ 用于创建 BoundSql, mapper.xml 文件中的 sql 语句会被解析成 BoundSql 对
象,经过解析 BoundSql 包含的语句最终仅仅包含?占位符,可以直接提交给数据库执
行;

​ 需要特别注意的是 Configuration 对象在 MyBatis 中是单例的,生命周期是应用级的,换句话说只要 MyBatis 运行 Configuration 对象就会独一无二的存在;在 MyBatis 中仅在
org.apache.ibatis.builder.xml.XMLConfigBuilder.XMLConfigBuilder(XPathParser, String, Properties)中有实例化 configuration 对象的代码。

代码如下

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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/**
* @author Clinton Begin
* mybatis 最重要的一个配置 保存着mybatis的关键配置信息
*/
public class Configuration {
/**
* mybatis 环境相关配置
*/
protected Environment environment;
/**
* 允许在嵌套语句中使用分页(RowBounds)默认值False
*/
protected boolean safeRowBoundsEnabled;

protected boolean safeResultHandlerEnabled = true;
/**
* 否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。 默认false
*/
protected boolean mapUnderscoreToCamelCase;
/**
* 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载(参考lazyLoadTriggerMethods).
*/
protected boolean aggressiveLazyLoading;
/**
* 是否允许单一语句返回多结果集(需要兼容驱动)。
*/
protected boolean multipleResultSetsEnabled = true;
/**
* 允许 JDBC 支持自动生成主键,需要驱动兼容。
* 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。
*/
protected boolean useGeneratedKeys;
/**
* 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。
*/
protected boolean useColumnLabel = true;
/**
* 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存。
*/
protected boolean cacheEnabled = true;
/**
* 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,
* 这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。
*/
protected boolean callSettersOnNulls;
/**
* 允许使用方法签名中的名称作为语句参数名称。
* 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。(从3.4.1开始)
*/
protected boolean useActualParamName = true;
/**
* 当返回行的所有列都是空时,MyBatis默认返回null。
* 当开启这个设置时,MyBatis会返回一个空实例。
* 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始)
*/
protected boolean returnInstanceForEmptyRow;
/**
* 指定 MyBatis 增加到日志名称的前缀。
*/
protected String logPrefix;
/**
* 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。
*/
protected Class<? extends Log> logImpl;
/**
* 指定VFS的实现
*/
protected Class<? extends VFS> vfsImpl;
/**
* MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。
* 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。
* 若设置值为 STATEMENT,本地会话仅用在语句执行上,
* 对相同 SqlSession 的不同调用将不会共享数据。
*/
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
/**
* 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。
* 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
*/
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
/**
* 指定哪个对象的方法触发一次延迟加载。
*/
protected Set<String> lazyLoadTriggerMethods = new HashSet<>(Arrays.asList("equals", "clone", "hashCode", "toString"));
/**
* 设置超时时间,它决定驱动等待数据库响应的秒数。
*/
protected Integer defaultStatementTimeout;
/**
* 为驱动的结果集获取数量(fetchSize)设置一个提示值。此参数只可以在查询设置中被覆盖。
*/
protected Integer defaultFetchSize;

protected ResultSetType defaultResultSetType;
/**
* 配置默认的执行器。
* SIMPLE 就是普通的执行器;
* REUSE 执行器会重用预处理语句(prepared statements);
* BATCH 执行器将重用语句并执行批量更新。
*/
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
/**
* 指定 MyBatis 应如何自动映射列到字段或属性。
* NONE 表示取消自动映射;
* PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。
* FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
*/
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
/**
* 指定发现自动映射目标未知列(或者未知属性类型)的行为。
* NONE: 不做任何反应
* WARNING: 输出提醒日志 (‘org.apache.ibatis.session.AutoMappingUnknownColumnBehavior’ 的日志等级必须设置为 WARN)
* FAILING: 映射失败 (抛出 SqlSessionException)
*/
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;

protected Properties variables = new Properties();
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
protected ObjectFactory objectFactory = new DefaultObjectFactory();
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
/**
* 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。
*/
protected boolean lazyLoadingEnabled = false;
/**
* 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。
*/
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL

protected String databaseId;
/**
* 指定一个提供Configuration实例的类。
* 这个被返回的Configuration实例用来加载被反序列化对象的懒加载属性值。
* 这个类必须包含一个签名方法static Configuration getConfiguration(). (从 3.2.3 版本开始)
*
* @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300 (google code)</a>
*/
protected Class<?> configurationFactory;
/**
* mapper 接口动态代理工厂类的注册中心。在 MyBatis 中,
* 通过mapperProxy 实现 InvocationHandler 接口,
* MapperProxyFactory 用于生成动态代理的实例对象
*
*/
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
/**
* 拦截器链
*/
protected final InterceptorChain interceptorChain = new InterceptorChain();
/**
* 类型处理器注册中心
*/
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
/**
* 类型别名注册
*/
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
/**
* 方言驱动注册
*/
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();

/**
* mapper文件的声明
* MappedStatement用于存储 mapper.xml 文件中的 select、insert、update 和 delete 节点,同时还包含了这些节点的很多重要属性;
*/
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
//用于解析 mapper.xml 文件中的 resultMap 节点,使用 ResultMapping 来封装id, result 等子元素;
protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");

protected final Set<String> loadedResources = new HashSet<>();
protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");

protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<>();

/*
* A map holds cache-ref relationship. The key is the namespace that
* references a cache bound to another namespace and the value is the
* namespace which the actual cache is bound to.
*/
protected final Map<String, String> cacheRefMap = new HashMap<>();

配置加载过程

Mybatis初始化流程,其实就是组装重量级All-In-One对象Configuration的过程,主要分为系统环境参数初始化和Mapper映射初始化,其中Mapper映射初始化尤为重要。

配置文件解析

1
2
inputStream = Resources.getResourceAsStream("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream)

进入方法看下:

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
/**
* 构建SqlSessionFactory
* @param inputStream mybatis-config.xml文件流
* @param environment 环境
* @param properties 属性信息
* @return
*/
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}


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

parser.parse()方法,已经返回了组装完毕的Configuration对象。

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
 /**
* 解析配置文件
* @return
*/
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
/**
* 解析Configuration配置
* @param root
*/
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
//解析properties属性
propertiesElement(root.evalNode("properties"));
//解析settings配置
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
//解析typeAliases配置
typeAliasesElement(root.evalNode("typeAliases"));
//解析插件配置
pluginElement(root.evalNode("plugins"));
//解析objectFactory配置
objectFactoryElement(root.evalNode("objectFactory"));
//解析objectWrapperFactory配置
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//解析reflectorFactory配置
reflectorFactoryElement(root.evalNode("reflectorFactory"));
//设置元素配置
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//解析环境environments
environmentsElement(root.evalNode("environments"));
//解析数据库ID配置
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析类型转换器
typeHandlerElement(root.evalNode("typeHandlers"));
//sql mapper.xml 配置解析
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}

以上代码,对mybatis-config.xml配置文件内的元素,使用XPath进行逐一读取。

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc.properties">
<property name="username" value="root" />
<property name="password" value="123" />
</properties>
<settings>
<setting name="localCacheScope" value="STATEMENT"/>
<setting name="cacheEnabled" value="false" />
<setting name="lazyLoadingEnabled" value="true" />
<setting name="multipleResultSetsEnabled" value="true" />
<setting name="useColumnLabel" value="true" />
<setting name="useGeneratedKeys" value="false" />
<setting name="defaultExecutorType" value="REUSE" />
<setting name="defaultStatementTimeout" value="25000" />
</settings>
<typeAliases>
<typeAlias alias="Student" type="com.mybatis3.domain.Student" />
<typeAlias alias="Teacher" type="com.mybatis3.domain.Teacher" />
</typeAliases>
<typeHandlers>
<typeHandler handler="com.mybatis3.typehandlers.PhoneTypeHandler" />
</typeHandlers>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/mybatis3/mappers/StudentMapper.xml" />
<mapper resource="com/mybatis3/mappers/TeacherMapper.xml" />
</mappers>
</configuration>

mapper配置文件解析

其中最关键的一行代码 mapperElement(root.evalNode(“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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
* 解析核心mapper.xml 配置
*
* @param parent
* @throws Exception
*/
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
//解析resource属性
String resource = child.getStringAttribute("resource");
//解析URL属性
String url = child.getStringAttribute("url");
//解析class属性
String mapperClass = child.getStringAttribute("class");
//resource属性不为空url为空
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
//获取mapper.xml的路径并转换为流
InputStream inputStream = Resources.getResourceAsStream(resource);
//创建Mapper文件解析器
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
//解析mapper配置文件
mapperParser.parse();
//resource为空URL不为空
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
//根据URL获取流
InputStream inputStream = Resources.getUrlAsStream(url);
//创建Mapper文件解析器
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
//解析mapper配置文件
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
//获取Mapper 的接口
Class<?> mapperInterface = Resources.classForName(mapperClass);
//添加mapper接口到配置中
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}


/**
* 解析mapper 配置文件
*/
public void parse() {
//mapper文件未被加载
if (!configuration.isResourceLoaded(resource)) {
//配置 mapper 信息
configurationElement(parser.evalNode("/mapper"));
//加载mapper
configuration.addLoadedResource(resource);
//构建mapper的命名控件
bindMapperForNamespace();
}
//解析结果集配置
parsePendingResultMaps();
//缓存配置解析
parsePendingCacheRefs();
//MapperStatement 解析
parsePendingStatements();
}
配置configuration
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
/**
* configuration
* @param context
*/
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
//参数配置解析
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//结果元素解析
resultMapElements(context.evalNodes("/mapper/resultMap"));
//sql元素解析
sqlElement(context.evalNodes("/mapper/sql"));
//编译 StatementFromContext 添加node 到 incompleteStatements
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}

逐一读取Mapper.xml文件内的各个元素。为了更为直观的了解xml元素至Mybatis的内部数据结构。

​ 这些Xml配置元素,Mybatis将它们分别封装成了ParameterMap、ParameterMapping、ResultMap、ResultMapping、MappedStatement、BoundSql等内部数据结构对象。

这些数据库结构对象,均放置于Configuration内部保存起来。

1
2
3
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");

评论