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

該系列文檔是本人在學習 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的初始化

在MyBatis初始化過程當中,大體會有如下幾個步驟:spring

  1. 建立Configuration全局配置對象,會往TypeAliasRegistry別名註冊中心添加Mybatis須要用到的相關類,並設置默認的語言驅動類爲XMLLanguageDriversql

  2. 加載mybatis-config.xml配置文件、Mapper接口中的註解信息和XML映射文件,解析後的配置信息會造成相應的對象並保存到Configuration全局配置對象中數據庫

  3. 構建DefaultSqlSessionFactory對象,經過它能夠建立DefaultSqlSession對象,MyBatis中SqlSession的默認實現類apache

由於整個初始化過程涉及到的代碼比較多,因此拆分紅了四個模塊依次對MyBatis的初始化進行分析:session

因爲在MyBatis的初始化過程當中去解析Mapper接口與XML映射文件涉及到的篇幅比較多,XML映射文件的解析過程也比較複雜,因此才分紅了後面三個模塊,逐步分析,這樣便於理解mybatis

初始化(一)之加載mybatis-config.xml

本文主要分享的是在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的全局配置對象,保存全部的配置與初始化過程所產生的對象

SqlSessionFactoryBuilder

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方法主要作了三件事:

  1. 建立XMLConfigBuilder對象,生成XPathParser配置文件解析器對象和Configuration全局配置對象
  2. 經過XMLConfigBuilder對象解析XML映射文件,配置信息、生成的相應對象都會保存至Configuration全局配置對象中
  3. 構建一個DefaultSqlSessionFactory對象

XMLConfigBuilder

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;
}
  1. 首先會進入XPathParser的構造方法,將XML配置文件解析成org.w3c.dom.Document對象,這裏傳入了XMLMapperEntityResolver做爲解析實例對象,其中使用到MyBatis本地的DTD文件

  2. 而後進入XMLConfigBuilder的另外一個構造方法,會先建立一個Configuration全局配置對象,初始化一些對象

parse方法

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官方文檔的配置說明

上面涉及到的解析方法就不一一列出來了,這裏作個簡單的描述,具體請閱讀這個類😈😈😈,我已經作好了註釋

propertiesElement方法

  1. 解析 <properties /> 標籤,成 Properties 對象。
  2. 覆蓋 configuration 中的 Properties 對象到上面的結果。
  3. 設置結果到 parserconfiguration 中。

settingsAsProperties方法

  1. <setting /> 標籤解析爲 Properties 對象

  2. 接下來會根據該屬性對象加載用戶自定義的VFS實現類和Log實現類,並將配置信息配置到Configuration全局配置對象中

typeAliasesElement方法

  1. <typeAliases /> 的子標籤進行解析
  2. 若是是<package />字標籤,則對其配置的name包名進行解析,將別名去Class對象進行映射往TypeAliasRegistry註冊,調用TypeAliasRegistry.registerAliases方法
  3. 不然就是配置的單個別名配置,進行解析並添加到TypeAliasRegistry中

pluginElement方法

  1. <plugins /> 的子標籤進行解析
  2. 生成對應的Interceptor攔截器對象並設置配置的屬性
  3. 將攔截器添加到 Configuration 全局配置的 InterceptorChain 攔截器調用鏈中,後續會講到,例如com.github.pagehelper.PageInterceptor分頁插件

mapperElement方法

在該方法中會解析Mapper接口,建立對應的MapperProxyFactory動態代理工廠,同時也會解析Mapper接口對應的XML映射文件

整個的解析過程涉及到的代碼有點多,在下一個模塊《MyBatis初始化(二)之加載Mapper接口與XML映射文件》中進行分析

Configuration

org.apache.ibatis.session.Configuration:MyBatis的全局配置對象,保存全部的配置與初始化過程所產生的對象,內容有點多,這裏就不列出來了,參考:Configuration.java

總結

MyBatis會在SqlSessionFactoryBuilder構造器中根據mybatis-config.xml配置文件初始化Configuration全局配置對象,而後建立對應的DefaultSqlSessionFactory工廠

在解析配置文件的過程當中,MyBatis的配置都會保存在Configuration對象中,對Mapper接口和XML映射文件進行解析生成相應的對象都會保存在其中

參考文章:芋道源碼《精盡 MyBatis 源碼分析》

相關文章
相關標籤/搜索