mybatis源码-解析配置文件(二)之解析的流程
@
1. 简介
在之前的文章《mybatis 初步使用(IDEA的Maven项目, 超详细)》中, 讲解了mybatis
的初步使用, 并总结了以下mybatis
的执行流程:
- 通过 Resources 工具类读取 mybatis-config.xml, 存入 Reader;
- SqlSessionFactoryBuilder 使用上一步获得的 reader 创建 SqlSessionFactory 对象;
- 通过 sqlSessionFactory 对象获得 SqlSession;
- SqlSession对象通过 *Mapper 方法找到对应的 SQL 语句, 执行 SQL 查询。
- 底层通过 JDBC 查询后获得 ResultSet, 对每一条记录, 根据resultMap的映射结果映射到 Student 中, 返回 List。
- 最后记得关闭 SqlSession
本系列文章深入讲解第 2 步, 解析配置文件。
2. 配置文件解析流程分析
2.1 调用
配置文件的解析过程对应的是以下的代码:
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
很简单的两句代码:
- 通过
mybatis
的资源类Resources
读入“mybatis-config.xml”
文件; - 使用
SqlSessionFactoryBuilder
类生成我们需要的SqlSessionFactory
类;(真正的解析只有这一过程)
2.2 解析的目的
要理解配置文件的解析过程, 首先要明白解析的目的是什么, 从最直观的调用代码来看, 是获得SqlSessionFactory
。
但是, 从源代码来看, 更本质的应该这么说:
mybatis解析配置文件最本质的目的是为了获得
Configuration
对象
Configuration
对象, 可以理解是mybatis
的XML
文件在程序中的化身。
2.3 XML 解析流程
build(reader)
函数里面包含着SqlSessionFactory
的创建逻辑。
从客户端调用build(reader)
函数到返回SqlSessionFactory
, 可以用如下的时序图表示:
下面来看看各个步骤, 请记住,mybatis解析配置文件的本质就是获得Configuration
对象。
2.3.1 build(parser)
其最终调用以下的方法
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
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.
}
}
}
该方法:
- 创建
XMLConfigBuilder
对象; - 使用
XMLConfigBuilder
对象的方法parse()
来获得Confiuration
对象; - 通过
build(configuration)
, 使用Confiuration
对象创建相应的SqlSessionFactory
对象。
2.3.2 new XMLConfigBuilder(...);
new XMLConfigBuilder(reader, environment, properties)
方法, 从字面上来理解就是创建一个XMLConfigBuilder
对象。
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) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
XMLConfigBuilder
类继承于BaseBuilder
类, super(new Configuration())
对应的方法:
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
也就是给BaseBuilder
类的各个成员变量赋值而已。
里面的XpathParser对象是通过new XPathParser(reader, true, props, new XMLMapperEntityResolver())
方法而来的。
2.3.3 new XPathParser(...)
new XPathParser(reader, true, props, new XMLMapperEntityResolver())
就是创建XpathParser
的过程。
public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(reader));
}
调用了以下两个函数:
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
注意这两个函数是有先后顺序的, createDocument
函数务必在commonConstructor
函数之后执行。
createDocument
函数, 其实就是通过 DOM 解析 XML 文件的过程中的几个步骤,获得document
, 具体可以参见 「mybatis 解析配置文件(一)之XML的DOM解析方式」, 里面提到了 Java 中使用 DOM 解析 XML 的步骤, 大致如下:
- 创建
DocumentBuilderFactory
对象;- 通过
DocumentBuilderFactory
创建DocumentBuilder
对象;- 通过
DocumentBuilder
, 从文件或流中创建通过Document
对象;- 创建
XPathFactory
对象, 并通过XPathFactory
创建XPath
对象;- 通过
XPath
解析出XPathExpression
对象;- 使用
XPathExpression
在文档中搜索出相应的节点。
刚刚提到的两个函数, 已经完成了前4部分, 获得了Document
对象, Xpath
对象, 并返回后将其赋值给了相应的成员变量。
也就是说, 到了这一步, 我们已经获得了XpathParser
对象, 该对象中已经含有 mybatis-config.xml 文件对应的 Document Object, 即document
和xpath
。 通过document
和xpath
,我们可以对 mybatis-config.xml 进行后两部操作操作。
后面几个步骤, 是在XMLConfiguration
对象的parse()
函数中使用到, 详情见 2.3.5。
2.3.4 new Configuration()
之前提到过, 配置文件解析的本质就是获得Configuration
对象。
现在, Configuration
对象在解析的过程中第一次出现了。
那我们就可以返回这个对象了?
当然不是, 这个对象现在只是创建, 后续还有很多成员变量需要根据 XML 配置文件解析后来赋值。
2.3.5 parser.parse()
这里的parser
是XMLConfigBuilder
对象。
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
这个函数返回的Configuration
对象就是最终写入SqlSessionFatory
对应成员变量的对象。
由于配置文件解析的本质就是获得Configuration
对象, 因此, 这个函数就是解析的核心。
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
其对应的过程就是解析 XML 配置文件中 properties, settings, typeAliases, plugins, objectFactory, objectWrapperFactory, reflectorFactory, environments, databaseIdProvider, typeHandlers, mappers, 这些子节点。
其中的evalNode
函数, 在其函数过程中, 会调用XParhParser
中的函数, 对 xml 节点进行解析:
private Object evaluate(String expression, Object root, QName returnType) {
try {
return xpath.evaluate(expression, root, returnType);
} catch (Exception e) {
throw new BuilderException("Error evaluating XPath. Cause: " + e, e);
}
}
以上过程就是我们 2.3.3 中提到的第 5, 6 步过程。
具体的在后续的文章中在深入了解。
2.3.6 build(configuration)
该函数就是创建一个具体的SqlSessionFactory
对象。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}
就是创建DefaultSqlSessionFactory
对象, 并将configuration
赋值给相应的成员变量。
更具体的解析配置的过程, 后续分享。
一起学 mybatis
你想不想来学习 mybatis? 学习其使用和源码呢?那么, 在博客园关注我吧!!
我自己打算把这个源码系列更新完毕, 同时会更新相应的注释。快去 star 吧!!
mybatis源码-解析配置文件(二)之解析的流程的更多相关文章
- MyBatis 源码分析 - 配置文件解析过程
* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...
- Mybatis源码阅读-配置文件及映射文件解析
Mybatis源码分析: 1.配置文件解析: 1.1源码阅读入口: org.apache.ibatis.builder.xml.XMLConfigBuilder.parse(); 功能:解析全局配置文 ...
- Sentinel-Go 源码系列(二)|初始化流程和责任链设计模式
上节中我们知道了 Sentinel-Go 大概能做什么事情,最简单的例子如何跑起来 其实我早就写好了本系列的第二篇,但迟迟没有发布,感觉光初始化流程显得有些单一,于是又补充了责任链模式,二合一,内容显 ...
- MyBatis源码分析-SQL语句执行的完整流程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
- Android7.0 Phone应用源码分析(二) phone来电流程分析
接上篇博文:Android7.0 Phone应用源码分析(一) phone拨号流程分析 今天我们再来分析下Android7.0 的phone的来电流程 1.1TelephonyFramework 当有 ...
- mybatis源码分析(二)------------配置文件的解析
这篇文章中,我们将讲解配置文件中 properties,typeAliases,settings和environments这些节点的解析过程. 一 properties的解析 private void ...
- 浩哥解析MyBatis源码(十二)——binding绑定模块之MapperRegisty
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6758456.html 1.回顾 之前解析了解析模块parsing,其实所谓的解析模块就是为 ...
- 浩哥解析MyBatis源码(十一)——Parsing解析模块之通用标记解析器(GenericTokenParser)与标记处理器(TokenHandler)
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6724223.html 1.回顾 上面的几篇解析了类型模块,在MyBatis中类型模块包含的 ...
- mybatis源码阅读-MappedStatement各个属性解析过程(八)
调用方 类org.apache.ibatis.builder.xml.XMLMapperBuilder private void configurationElement(XNode context) ...
- mybatis 源码分析(二)mapper 初始化
mybatis 的初始化还是相对比较复杂,但是作者在初始化过程中使用了多种设计模式,包括建造者.动态代理.策略.外观等,使得代码的逻辑仍然非常清晰,这一点非常值得我们学习: 一.mapper 初始化主 ...
随机推荐
- bootstrap使用基础
1.为了适应跨屏浏览,Bootstrap为单元格预定义了4种class ,分别对应于手机.ipad.笔记本电脑.台式机. <div class="row"> <d ...
- oracle--ORA-00054: 资源正忙, 但指定以 NOWAIT 方式获取资源, 或者超时失效
SELECT sid, serial#, username, osuser FROM v$session where sid in(select session_id from v$locked_ob ...
- Azure 实例元数据服务
Azure 实例元数据服务提供有关可用于管理和配置虚拟机的正在运行的虚拟机实例的信息. 这包括 SKU.网络配置和即将发生的维护事件等信息. 若要详细了解可用信息类型,请参阅元数据类别. Azure ...
- 【JAVA】使用 jedis操作redis——连接、存储数据、切库等
本篇运用Java调用jedis包(jedis在线文档API ),做简单操作实例. 安装jedis 1. 2.9.0 jar 版本下载: jedis-2.9.0.jar 2. 新建项目,添加该驱动包 连 ...
- AfxBeginThread
用户界面线程和工作者线程都是由AfxBeginThread创建的.现在,考察该函数:MFC提供了两个重载版的AfxBeginThread,一个用于用户界面线程,另一个用于工作者线程,分别有如下的原型和 ...
- redis 配置文件示例
# redis 配置文件示例 # 当你需要为某个配置项指定内存大小的时候,必须要带上单位,# 通常的格式就是 1k 5gb 4m 等酱紫:## 1k => 1000 bytes# 1kb =& ...
- 基础知识整理汇总 - Java学习(一)
java 语言规范及相关文档资源 Java源码:安装目录下 src.zip 文件 java文档:https://docs.oracle.com/en/java/ 语言规范:http://docs.or ...
- 【Ansible 文档】【译文】配置文件
这里说明一下配置文件的内容,原文地址:http://docs.ansible.com/ansible/latest/intro_configuration.html 这个与[Ansible 文档]配置 ...
- css多种方法实现已知宽度和未知宽度的元素水平垂直居中
// html <div class="box-wrapper"> <div class="box"> 内部box <p>更 ...
- 关于react的一点工作总结
首先,react是Facebook开发的一套前端框架,仅仅是MVC中的V.核心思想是“封装组件”,组件封装后可以作为一个独立的实体被引入到新的组件中,这样新的组件就又是一个实体了,由于组件的实现了可复 ...