該系列文檔是本人在學習 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 源碼分析》