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 初始化主 ...
随机推荐
- sql server 用户'sa'登录失败(错误18456)
转载于:http://thenear.blog.51cto.com/4686262/865544 用户'sa'登录失败(错误18456)解决方案图解 当我们在使用sql server 的时候可 ...
- CAC的Debian-8-64bit安装BBR正确方式是?
裝过三台debian 64 bit, CAC, 2歐, KVM虛擬機 做法都一樣 0. 有裝銳速記得先刪除, 免得換核心後, 銳速在扯後腿 1.換4.9版kernel 有正式版 別裝啥rc版, 4.9 ...
- 用于创建和管理 Azure 虚拟机的常用 PowerShell 命令
本文介绍一些可用于在 Azure 订阅中创建和管理虚拟机的 Azure PowerShell 命令. 若要获取特定命令行开关和选项的详细帮助,可以使用 Get-Help 命令. 有关安装最新版 Azu ...
- SQLSERVER文件组误脱机后如何联机
场景:在学习文件组的恢复过程中,通过 ALTER DATABASE TEST MODIFY FILE(NAME = SUBF,OFFLINE) 把文件组给弄脱机了.这时却发现脱机之前忘记备份了. 这时 ...
- Jboss 4.2.3配置与优化
1 Jboss内存优化 修改这个两参数,给jvm分配适当的内存,一般为服务器的3/4内存量,推荐至少使用4G内存. 另外添加两个参数 -XX:+UseParallelGC -XX:+UseP ...
- 【转载】sql注入之入门
原文在:https://smelond.com MySql基础语法 mysql无非就是增删改查 mysql数据库结构: 数据库 test,test1 表名 admin,manage 数据 id,use ...
- 小程序码B接口生成出错:场景内容包含非法字符
由于包含了非法字符,微信返回的字节不超过100字符,但是没有包含提示内容,因此很难识别发现问题所在
- Linux nmap命令详解
nmap,也就是Network Mapper,是Linux下的网络扫描和嗅探工具包. nmap是在网络安全渗透测试中经常会用到的强大的扫描器.功能之强大,不言而喻.下面介绍一下它的几种扫描命令.具体的 ...
- css图片替换方法
图片替换主要是指将文字替换成图片的技术,即在html语句中使用文字,浏览器显示时用对应的图片显示.其意义在于便于做网站优化(SEO),因为文字才是搜索引擎寻找的主要对象. https://www.cn ...
- 小程序报错数据传输长度为 xxx 已经超过最大长度 xxx
这个错误通常在通过base64给images赋值时发生,setData()微信设置的一次最大传输长度为1M,所有如果编码后的base64字符串长度超过了1M就会报这个错误. 如何解决? 我们可以采取曲 ...