1. 程式人生 > >精盡 MyBatis 原始碼分析 - MyBatis 初始化(一)之載入 mybatis-config.xml

精盡 MyBatis 原始碼分析 - MyBatis 初始化(一)之載入 mybatis-config.xml

> 該系列文件是本人在學習 Mybatis 的原始碼過程中總結下來的,可能對讀者不太友好,請結合我的原始碼註釋([Mybatis原始碼分析 GitHub 地址](https://github.com/liu844869663/mybatis-3)、[Mybatis-Spring 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring)、[Spring-Boot-Starter 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-boot-starter))進行閱讀 > > MyBatis 版本:3.5.2 > > MyBatis-Spring 版本:2.0.3 > > MyBatis-Spring-Boot-Starter 版本:2.1.4 ## MyBatis的初始化 在MyBatis初始化過程中,大致會有以下幾個步驟: 1. 建立`Configuration`全域性配置物件,會往`TypeAliasRegistry`別名註冊中心新增Mybatis需要用到的相關類,並設定預設的語言驅動類為`XMLLanguageDriver` 2. 載入`mybatis-config.xml`配置檔案、Mapper介面中的註解資訊和XML對映檔案,解析後的配置資訊會形成相應的物件並儲存到Configuration全域性配置物件中 3. 構建`DefaultSqlSessionFactory`物件,通過它可以建立`DefaultSqlSession`物件,MyBatis中`SqlSession`的預設實現類 因為整個初始化過程涉及到的程式碼比較多,所以拆分成了四個模組依次對MyBatis的初始化進行分析: - [**《MyBatis初始化(一)之載入mybatis-config.xml》**](https://www.cnblogs.com/lifullmoon/p/14015009.html) - **《MyBatis初始化(二)之載入Mapper介面與XML對映檔案》** - **《MyBatis初始化(三)之SQL初始化(上)》** - **《MyBatis初始化(四)之SQL初始化(下)》** 由於在MyBatis的初始化過程中去解析Mapper介面與XML對映檔案涉及到的篇幅比較多,XML對映檔案的解析過程也比較複雜,所以才分成了後面三個模組,逐步分析,這樣便於理解 ## 初始化(一)之載入mybatis-config.xml 本文主要分享的是在MyBatis初始化過程中,是如何載入`mybatis-config.xml`配置檔案的,配置描述請參考:[MyBatis官方文件的配置說明](https://mybatis.org/mybatis-3/zh/configuration.html) 初始化入口在`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的全域性配置物件,儲存所有的配置與初始化過程所產生的物件 ### SqlSessionFactoryBuilder `org.apache.ibatis.session.SqlSessionFactoryBuilder`:構建SqlSessionFactory工廠類,裡面定義了許多build過載方法,主要分為處理Reader和InputStream兩種檔案資源物件 我們來看看其中的一個`build`方法: ```java 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`方法主要做了三件事: 1. 建立`XMLConfigBuilder`物件,生成`XPathParser`配置檔案解析器物件和`Configuration`全域性配置物件 2. 通過`XMLConfigBuilder`物件解析XML對映檔案,配置資訊、生成的相應物件都會儲存至`Configuration`全域性配置物件中 3. 構建一個`DefaultSqlSessionFactory`物件 ### XMLConfigBuilder `org.apache.ibatis.builder.xml.XMLConfigBuilder`:根據配置檔案進行解析,開始Mapper介面與XML對映檔案的初始化,生成Configuration全域性配置物件 #### 構造方法 ```java 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; } ``` 1. 首先會進入`XPathParser`的構造方法,將XML配置檔案解析成`org.w3c.dom.Document`物件,這裡傳入了`XMLMapperEntityResolver`作為解析例項物件,其中使用到MyBatis本地的DTD檔案 2. 然後進入`XMLConfigBuilder`的另一個構造方法,會先建立一個Configuration全域性配置物件,初始化一些物件 #### parse方法 ```java 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> 解析
標籤 propertiesElement(root.evalNode("properties")); // <2> 解析 標籤,解析配置生成 Properties 物件 Properties settings = settingsAsProperties(root.evalNode("settings")); // 根據配置載入自定義 VFS 實現類 loadCustomVfs(settings); // 根據配置載入自定義的 Log 實現類 loadCustomLogImpl(settings); // <3> 解析
標籤,生成別名與類的對映關係 typeAliasesElement(root.evalNode("typeAliases")); // <4> 解析 標籤,新增自定義攔截器外掛 pluginElement(root.evalNode("plugins")); // <5> 解析 標籤,自定義例項工廠 objectFactoryElement(root.evalNode("objectFactory")); // <6> 解析 標籤,自定義 ObjectWrapperFactory 工廠,無預設實現 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); // <7> 解析
標籤,自定義 Reflector 工廠 reflectorFactoryElement(root.evalNode("reflectorFactory")); // 將 配置資訊新增到 Configuration 屬性 settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 // <8> 解析 標籤,自定義當前環境資訊 environmentsElement(root.evalNode("environments")); // <9> 解析 標籤,資料庫識別符號 databaseIdProviderElement(root.evalNode("databaseIdProvider")); // <10> 解析 標籤,自定義型別處理器 typeHandlerElement(root.evalNode("typeHandlers")); // <11> 解析 標籤,掃描Mapper介面並進行解析 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } } ``` 在`parse()`解析方法中,獲取到Document物件的``節點,然後呼叫`parseConfiguration`進行解析,依次解析以下標籤: `<1> `解析` `標籤,呼叫`propertiesElement`方法 `<2>`解析``標籤,解析配置生成 Properties 物件,呼叫`settingsAsProperties`方法 `<3> `解析``標籤,生成別名與類的對映關係,呼叫`typeAliasesElement`方法 `<4> `解析``標籤,新增自定義攔截器外掛,呼叫`pluginElement`方法 `<5> `解析``標籤,自定義例項工廠,呼叫`objectFactoryElement`方法 `<6>`解析` `標籤,自定義 ObjectWrapperFactory 工廠,呼叫`objectWrapperFactoryElement`方法 `<7> `解析``標籤,自定義 Reflector 工廠,呼叫`reflectorFactoryElement`方法 `<8> `解析``標籤,自定義當前環境資訊,呼叫`environmentsElement`方法 `<9>`解析``標籤,資料庫識別符號,呼叫`databaseIdProviderElement`方法 `<10>`解析``標籤,自定義型別處理器,呼叫`typeHandlerElement`方法 `<11> `解析``標籤,掃描Mapper介面並進行解析,呼叫`mapperElement`方法 關於MyBatis的配置描述請參考[MyBatis官方文件的配置說明](https://mybatis.org/mybatis-3/zh/configuration.html) 上面涉及到的解析方法就不一一列出來了,這裡做個簡單的描述,具體請閱讀這個類