精尽 MyBatis 源码分析 - MyBatis 初始化(一)之加载 mybatis-config.xml
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址、Mybatis-Spring 源码分析 GitHub 地址、Spring-Boot-Starter 源码分析 GitHub 地址)进行阅读
MyBatis 版本:3.5.2
MyBatis-Spring 版本:2.0.3
MyBatis-Spring-Boot-Starter 版本:2.1.4
MyBatis的初始化
在MyBatis初始化过程中,大致会有以下几个步骤:
创建
Configuration
全局配置对象,会往TypeAliasRegistry
别名注册中心添加Mybatis需要用到的相关类,并设置默认的语言驱动类为XMLLanguageDriver
加载
mybatis-config.xml
配置文件、Mapper接口中的注解信息和XML映射文件,解析后的配置信息会形成相应的对象并保存到Configuration全局配置对象中构建
DefaultSqlSessionFactory
对象,通过它可以创建DefaultSqlSession
对象,MyBatis中SqlSession
的默认实现类
因为整个初始化过程涉及到的代码比较多,所以拆分成了四个模块依次对MyBatis的初始化进行分析:
- 《MyBatis初始化(一)之加载mybatis-config.xml》
- 《MyBatis初始化(二)之加载Mapper接口与XML映射文件》
- 《MyBatis初始化(三)之SQL初始化(上)》
- 《MyBatis初始化(四)之SQL初始化(下)》
由于在MyBatis的初始化过程中去解析Mapper接口与XML映射文件涉及到的篇幅比较多,XML映射文件的解析过程也比较复杂,所以才分成了后面三个模块,逐步分析,这样便于理解
初始化(一)之加载mybatis-config.xml
本文主要分享的是在MyBatis初始化过程中,是如何加载mybatis-config.xml
配置文件的,配置描述请参考:MyBatis官方文档的配置说明
初始化入口在org.apache.ibatis.session.SqlSessionFactoryBuilder
构造器中,因为需要通过mybatis-config.xml
配置文件构建一个SqlSessionFactory工厂,用于创建SqlSession会话
主要涉及到以下几个类:
org.apache.ibatis.session.SqlSessionFactoryBuilder
:用于构建SqlSessionFactory工厂org.apache.ibatis.builder.xml.XMLConfigBuilder
:根据配置文件进行解析,开始Mapper接口与XML映射文件的初始化,生成Configuration全局配置对象org.apache.ibatis.builder.xml.XMLMapperBuilder
:继承BaseBuilder抽象类,用于解析XML映射文件内的标签org.apache.ibatis.session.Configuration
:MyBatis的全局配置对象,保存所有的配置与初始化过程所产生的对象
SqlSessionFactoryBuilder
org.apache.ibatis.session.SqlSessionFactoryBuilder
:构建SqlSessionFactory工厂类,里面定义了许多build重载方法,主要分为处理Reader和InputStream两种文件资源对象
我们来看看其中的一个build
方法:
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
/**
* 构造 SqlSessionFactory 对象
*
* @param reader Reader 对象
* @param environment 环境
* @param properties Properties 变量
* @return SqlSessionFactory 对象
*/
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
/*
* <1> 创建 XMLConfigBuilder 对象
* 会生成一个 XPathParser,包含 Document 对象
* 会创建一个 Configuration 全局配置对象
*/
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
/*
* <2> 解析 XML 文件并配置到 Configuration 全局配置对象中
* <3> 创建 DefaultSqlSessionFactory 对象
*/
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
}
build
方法主要做了三件事:
- 创建
XMLConfigBuilder
对象,生成XPathParser
配置文件解析器对象和Configuration
全局配置对象 - 通过
XMLConfigBuilder
对象解析XML映射文件,配置信息、生成的相应对象都会保存至Configuration
全局配置对象中 - 构建一个
DefaultSqlSessionFactory
对象
XMLConfigBuilder
org.apache.ibatis.builder.xml.XMLConfigBuilder
:根据配置文件进行解析,开始Mapper接口与XML映射文件的初始化,生成Configuration全局配置对象
构造方法
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// <1> 创建 Configuration 对象
super(new Configuration());
// 创建一个当前线程的上下文,记录错误信息
ErrorContext.instance().resource("SQL Mapper Configuration");
// <2> 设置 Configuration 的 variables 属性
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
首先会进入
XPathParser
的构造方法,将XML配置文件解析成org.w3c.dom.Document
对象,这里传入了XMLMapperEntityResolver
作为解析实例对象,其中使用到MyBatis本地的DTD文件然后进入
XMLConfigBuilder
的另一个构造方法,会先创建一个Configuration全局配置对象,初始化一些对象
parse方法
public Configuration parse() {
// <1.1> 若已解析,抛出 BuilderException 异常
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
// <1.2> 标记已解析
parsed = true;
// <2> 解析 XML configuration 节点
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
// <1> 解析 <properties /> 标签
propertiesElement(root.evalNode("properties"));
// <2> 解析 <settings /> 标签,解析配置生成 Properties 对象
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 根据配置加载自定义 VFS 实现类
loadCustomVfs(settings);
// 根据配置加载自定义的 Log 实现类
loadCustomLogImpl(settings);
// <3> 解析 <typeAliases /> 标签,生成别名与类的映射关系
typeAliasesElement(root.evalNode("typeAliases"));
// <4> 解析 <plugins /> 标签,添加自定义拦截器插件
pluginElement(root.evalNode("plugins"));
// <5> 解析 <objectFactory /> 标签,自定义实例工厂
objectFactoryElement(root.evalNode("objectFactory"));
// <6> 解析 <objectWrapperFactory /> 标签,自定义 ObjectWrapperFactory 工厂,无默认实现
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// <7> 解析 <reflectorFactory /> 标签,自定义 Reflector 工厂
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 将 <settings /> 配置信息添加到 Configuration 属性
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
// <8> 解析 <environments /> 标签,自定义当前环境信息
environmentsElement(root.evalNode("environments"));
// <9> 解析 <databaseIdProvider /> 标签,数据库标识符
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// <10> 解析 <typeHandlers /> 标签,自定义类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
// <11> 解析 <mappers /> 标签,扫描Mapper接口并进行解析
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
在parse()
解析方法中,获取到Document对象的<configuration />
节点,然后调用parseConfiguration
进行解析,依次解析以下标签:
<1>
解析<properties />
标签,调用propertiesElement
方法
<2>
解析<settings />
标签,解析配置生成 Properties 对象,调用settingsAsProperties
方法
<3>
解析<typeAliases />
标签,生成别名与类的映射关系,调用typeAliasesElement
方法
<4>
解析<plugins />
标签,添加自定义拦截器插件,调用pluginElement
方法
<5>
解析<objectFactory />
标签,自定义实例工厂,调用objectFactoryElement
方法
<6>
解析<objectWrapperFactory />
标签,自定义 ObjectWrapperFactory 工厂,调用objectWrapperFactoryElement
方法
<7>
解析<reflectorFactory />
标签,自定义 Reflector 工厂,调用reflectorFactoryElement
方法
<8>
解析<environments />
标签,自定义当前环境信息,调用environmentsElement
方法
<9>
解析<databaseIdProvider />
标签,数据库标识符,调用databaseIdProviderElement
方法
<10>
解析<typeHandlers />
标签,自定义类型处理器,调用typeHandlerElement
方法
<11>
解析<mappers />
标签,扫描Mapper接口并进行解析,调用mapperElement
方法
关于MyBatis的配置描述请参考MyBatis官方文档的配置说明
上面涉及到的解析方法就不一一列出来了,这里做个简单的描述,具体请阅读这个类
精尽 MyBatis 源码分析 - MyBatis 初始化(一)之加载 mybatis-config.xml的更多相关文章
- 6.Sentinel源码分析—Sentinel是如何动态加载配置限流的?
Sentinel源码解析系列: 1.Sentinel源码分析-FlowRuleManager加载规则做了什么? 2. Sentinel源码分析-Sentinel是如何进行流量统计的? 3. Senti ...
- Tomcat源码分析三:Tomcat启动加载过程(一)的源码解析
Tomcat启动加载过程(一)的源码解析 今天,我将分享用源码的方式讲解Tomcat启动的加载过程,关于Tomcat的架构请参阅<Tomcat源码分析二:先看看Tomcat的整体架构>一文 ...
- angular源码分析:angular的整个加载流程
在前面,我们讲了angular的目录结构.JQLite以及依赖注入的实现,在这一期中我们将重点分析angular的整个框架的加载流程. 一.从源代码的编译顺序开始 下面是我们在目录结构哪一期理出的an ...
- laravel框架源码分析(一)自动加载
一.前言 使用php已有好几年,laravel的使用也是有好长时间,但是一直对于框架源码的理解不深,原因很多,归根到底还是php基础不扎实,所以源码看起来也比较吃力.最近有时间,所以开启第5.6遍的框 ...
- Spring源码分析(二十一)加载BeanFactory
摘要: 本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 目录 一.定制化BeanFactory 二.加载BeanDefinit ...
- springmvc源码分析----入门看springmvc的加载过程
接上一篇我们写的入门---http://www.cnblogs.com/duanxiaojun/p/6591448.html 今天从这个门里进去我们看springmvc是如何在容器启动的时候将各个模块 ...
- springmvc源码分析——入门看springmvc的加载过程
本文将分析springmvc是如何在容器启动的时候将各个模块加载完成容器的创建的. 我知道在web.xml文件中我们是这样配置springmvc的: 可以看到,springmvc的核心控制器就是Dis ...
- 【Spring源码分析系列】bean的加载
前言 以 BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beans.xml"));为例查看bean的加载过 ...
- MyBatis源码分析(3)—— Cache接口以及实现
@(MyBatis)[Cache] MyBatis源码分析--Cache接口以及实现 Cache接口 MyBatis中的Cache以SPI实现,给需要集成其它Cache或者自定义Cache提供了接口. ...
- 精尽MyBatis源码分析 - MyBatis初始化(四)之 SQL 初始化(下)
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
随机推荐
- Helium文档12-WebUI自动化-go_to在当前的Web浏览器窗口中打开指定的URL
前言 go_to在当前的Web浏览器窗口中打开指定的URL 入参介绍 url def go_to(url): """ :param url: URL to open. : ...
- python第三章:函数
在前面章节中,介绍了一些input(),print(),len()等内建函数,还有random,math等标准库相关函数,这些都是可以直接使用的,但是很多时候,我们也是可以编写自己的函数. 看个例子: ...
- Java中的String到底占用多大的内存空间?你所了解的可能都是错误的!!
写在前面 最近小伙伴加群时,我总是问一个问题:Java中的String类占用多大的内存空间?很多小伙伴的回答着实让我哭笑不得,有说不占空间的,有说1个字节的,有说2个字节的,有说3个字节的,有说不知道 ...
- Python 从入门到精通:一个月就够了
毫无疑问,Python 是当下最火的编程语言之一.对于许多未曾涉足计算机编程的领域「小白」来说,深入地掌握 Python 看似是一件十分困难的事.其实,只要掌握了科学的学习方法并制定了合理的学习计划, ...
- c#封装ActiveX接口实践分析
ActiveX接口 是一个开放的集成平台,为开发人员.用户和 Web生产商提供了一个快速而简便的在 Internet 和 Intranet 创建程序集成和内容的方法. 使用 ActiveX, 可轻松方 ...
- Vue.js 获得兄弟元素,子元素,父元素(DOM操作)
e.target 是你当前点击的元素 e.currentTarget 是你绑定事件的元素 e.currentTarget.previousElementSibling.innerHTML 获得点击元素 ...
- JS删除微博
昨天晚上找回了10年注册的微博,现在瞅瞅,转发过很多傻吊的微博,关注了一堆营销号,不忍直视,动手删吧!开玩笑的,怎么可能手动! 查看自己的所有微博,F12----->console,负责下面代码 ...
- router-view组件在app.vue中渲染不出来怎么办
1.在app.vue使用router-view组件直接渲染 页面什么都没显示,可能问题出在路由配置上了,检查路由是否配置完好,路由挂载那里必须使用routes属性 2.在app.vue中router- ...
- oracle 存储过程深入学习与应用
对于存储过程已经有过一周的学习时间了,但是之学到一些皮毛,争取根据基础,熟练后能进行深入. --1.存储过程创建存储过程的语法: CREATE [ OR REPLACE ] PROCEDURE pro ...
- Go语言中的互斥锁和读写锁(Mutex和RWMutex)
目录 一.Mutex(互斥锁) 不加锁示例 加锁示例 二.RWMutex(读写锁) 并发读示例 并发读写示例 三.死锁场景 1.Lock/Unlock不是成对出现 2.锁被拷贝使用 3.循环等待 虽然 ...