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 初始化主 ...
随机推荐
- LeetCode题解之Univalued Binary Tree
1.题目描述 2.问题分析 遍历一遍树,然后将所有节点的数值放入到一个set中,最后检查set中元素的个数是否为1. 3.代码 bool isUnivalTree(TreeNode* root) { ...
- 购物商城学习--第二讲(maven工程介绍)
接下来第二讲介绍整体工程如何使用maven搭建的. 使用maven管理工程的好处: jar包的管理: 工程之间的依赖管理: 自动打包 maven常见打包方式:jar.war和pom三种.jar工程,是 ...
- [Python_7] Python Socket 编程
0. 说明 Python Socket 编程 1. TCP 协议 [TCP Server] 通过 netstat -ano 查看端口是否开启 # -*-coding:utf-8-*- "&q ...
- 【转】电脑运行命令CMD集锦
在win7系统里直接点开始,最左下面有个搜索框,在里面直接输CMD,回车就可以:或者win键+R键,出现对话框,输入CMD,回车就可以了: winver 检查Windows版本 wmimgmt.msc ...
- 【Alpha 冲刺】 6/12
今日任务总结 人员 今日原定任务 完成情况 遇到问题 贡献值 胡武成 建立数据库 未完成 表结构文档已设计好,服务器mysql刚配置完成,但是,SpringMVC框架还没有熟络,不清楚如何使用该框架去 ...
- Lombok 继承时应注意的点
lombok项目的产生就是为了省去我们手动创建getter和setter等基本方法的麻烦,它能够在我们编译源码的时候自动帮我们生成getter和setter等方法.即它最终能够达到的效果是:在源码中没 ...
- Vuex数据页面刷新丢失问题解决方案
用Vue做项目开发很久了,对于vuex能用.会用,但是因为状态脱离页面和刷新丢失两个原因,一直都有种抵触,特别是一些简单的数据都是通过query或者本地存储就解决了,然而对于一些复杂内容,不可避免的还 ...
- python第四十一课——析构函数
3.析构函数 格式:__del__(self): 作用: 在程序结束前将对象回收,释放资源的行为 演示析构函数的使用: class Animal: #定义构造函数 def __init__(self, ...
- BZOJ3105:[CQOI2013]新Nim游戏(线性基,贪心)
Description 传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同).两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴.可以只拿一根,也可以拿走整堆火柴 ...
- Android:你要的WebView与 JS 交互方式 都在这里了
前言 现在很多App里都内置了Web网页(Hybrid App),比如说很多电商平台,淘宝.京东.聚划算等等,如下图 上述功能是由Android的WebView实现的,其中涉及到Android客户端与 ...