该系列文档是本人在学习 Mybatis 的源码过程当中总结下来的,可能对读者不太友好,请结合个人源码注释(Mybatis源码分析 GitHub 地址、Mybatis-Spring 源码分析 GitHub 地址、Spring-Boot-Starter 源码分析 GitHub 地址)进行阅读html
MyBatis 版本:3.5.2java
MyBatis-Spring 版本:2.0.3git
MyBatis-Spring-Boot-Starter 版本:2.1.4github
在MyBatis初始化过程当中,大体会有如下几个步骤:spring
建立Configuration
全局配置对象,会往TypeAliasRegistry
别名注册中心添加Mybatis须要用到的相关类,并设置默认的语言驱动类为XMLLanguageDriver
sql
加载mybatis-config.xml
配置文件、Mapper接口中的注解信息和XML映射文件,解析后的配置信息会造成相应的对象并保存到Configuration全局配置对象中数据库
构建DefaultSqlSessionFactory
对象,经过它能够建立DefaultSqlSession
对象,MyBatis中SqlSession
的默认实现类apache
由于整个初始化过程涉及到的代码比较多,因此拆分红了四个模块依次对MyBatis的初始化进行分析:session
因为在MyBatis的初始化过程当中去解析Mapper接口与XML映射文件涉及到的篇幅比较多,XML映射文件的解析过程也比较复杂,因此才分红了后面三个模块,逐步分析,这样便于理解mybatis
本文主要分享的是在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的全局配置对象,保存全部的配置与初始化过程所产生的对象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
对象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全局配置对象,初始化一些对象
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官方文档的配置说明
上面涉及到的解析方法就不一一列出来了,这里作个简单的描述,具体请阅读这个类😈😈😈,我已经作好了注释
<properties />
标签,成 Properties 对象。configuration
中的 Properties 对象到上面的结果。parser
和 configuration
中。将 <setting />
标签解析为 Properties 对象
接下来会根据该属性对象加载用户自定义的VFS实现类和Log实现类,并将配置信息配置到Configuration全局配置对象中
<typeAliases />
的子标签进行解析<package />
字标签,则对其配置的name包名进行解析,将别名去Class对象进行映射往TypeAliasRegistry注册,调用TypeAliasRegistry.registerAliases
方法<plugins />
的子标签进行解析com.github.pagehelper.PageInterceptor
分页插件在该方法中会解析Mapper接口,建立对应的MapperProxyFactory动态代理工厂,同时也会解析Mapper接口对应的XML映射文件
整个的解析过程涉及到的代码有点多,在下一个模块《MyBatis初始化(二)之加载Mapper接口与XML映射文件》中进行分析
org.apache.ibatis.session.Configuration
:MyBatis的全局配置对象,保存全部的配置与初始化过程所产生的对象,内容有点多,这里就不列出来了,参考:Configuration.java
MyBatis会在SqlSessionFactoryBuilder
构造器中根据mybatis-config.xml
配置文件初始化Configuration
全局配置对象,而后建立对应的DefaultSqlSessionFactory
工厂
在解析配置文件的过程当中,MyBatis的配置都会保存在Configuration
对象中,对Mapper接口和XML映射文件进行解析生成相应的对象都会保存在其中
参考文章:芋道源码《精尽 MyBatis 源码分析》