天天看點

淺析MyBatis的配置加載流程

作者:Java小蟲
淺析MyBatis的配置加載流程

前言

在原始MyBatis的使用中,使用MyBatis時會先讀取配置檔案mybatis-config.xml為字元流或者位元組流,然後通過SqlSessionFactoryBuilder基于配置檔案的字元流或位元組流來建構SqlSessionFactory。

本篇文章将結合MyBatis源碼,對讀取配置檔案mybatis-config.xml和建構SqlSessionFactory的原理進行學習。

正文

原始MyBatis讀取配置檔案mybatis-config.xml和建構SqlSessionFactory的一個示例如下。

InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
複制代碼           

上述示例中的Resources工具類提供了方法可以讀取classpath下指定名字的檔案為字元流或者位元組流,這裡是使用了其提供的getResourceAsStream() 方法将mybatis-config.xml 檔案讀取為位元組流。SqlSessionFactoryBuilder是一個建造者,其提供了共計9個重載的build() 方法用于建構SqlSessionFactory,這9個build() 方法可以分為三類,概括如下。

  1. 基于配置檔案字元流建構SqlSessionFactory;
  2. 基于配置檔案位元組流建構SqlSessionFactory;
  3. 基于Configuration類建構SqlSessionFactory。

實際上,基于配置檔案字元流和基于配置檔案位元組流建構的方式,最終都是将字元流或位元組流解析并生成Configuration類,然後基于Configuration類建構SqlSessionFactory。

下面以基于配置檔案位元組流建構SqlSessionFactory的過程為例,對整個讀配置檔案的流程進行說明。上面的示例中調用的build() 方法如下所示。

public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
}
複制代碼           

上面被調用的重載的build() 方法如下所示。

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
        // XMLConfigBuilder會解析配置檔案并生成Configuration類
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        // 調用入參為Configuration的build()方法建構SqlSessionFactory并傳回
        return build(parser.parse());
    } catch (Exception e) {
        throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
        ErrorContext.instance().reset();
        try {
            inputStream.close();
        } catch (IOException e) {

        }
    }
}
複制代碼           

可以發現,配置檔案的解析是發生在XMLConfigBuilder的parse() 方法中,在檢視parse() 方法前,先看一下XMLConfigBuilder的類圖,如下所示。

淺析MyBatis的配置加載流程

通過XMLConfigBuilder的類圖可以知道,XMLConfigBuilder解析配置檔案是依靠XPathParser,而XPathParser是MyBatis提供的基于JAVA XPath的解析器。同時,XMLConfigBuilder内部維護了一個Configuration,通過XPathParser解析配置檔案得到的配置屬性均會豐富到Configuration中。

現在開始分析XMLConfigBuilder的parse() 方法,其實作如下所示。

public Configuration parse() {
    if (parsed) {
        throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
}
複制代碼           

要了解parse() 方法,最好是和一個實際的配置檔案進行對照,如下給出一個實際的配置檔案。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <setting name="useGeneratedKeys" value="true"/>
    </settings>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <package name="com.mybatis.learn.dao"/>
    </mappers>
</configuration>
複制代碼           

是以在parse() 方法中,首先是擷取配置檔案的configuration節點(根節點),然後将configuration節點傳入parseConfiguration() 方法,接着在parseConfiguration() 方法中會根據傳入的configuration節點依次擷取子節點并讀取子節點屬性,最後将擷取到的屬性豐富進Configuration。parseConfiguration() 方法實作如下所示。

private void parseConfiguration(XNode root) {
    try {
        propertiesElement(root.evalNode("properties"));
        Properties settings = settingsAsProperties(root.evalNode("settings"));
        loadCustomVfs(settings);
        loadCustomLogImpl(settings);
        typeAliasesElement(root.evalNode("typeAliases"));
        pluginElement(root.evalNode("plugins"));
        objectFactoryElement(root.evalNode("objectFactory"));
        objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
        reflectorFactoryElement(root.evalNode("reflectorFactory"));
        settingsElement(settings);
        // 豐富environments标簽及其子标簽的屬性到Configuration中
        environmentsElement(root.evalNode("environments"));
        databaseIdProviderElement(root.evalNode("databaseIdProvider"));
        typeHandlerElement(root.evalNode("typeHandlers"));
        // 豐富mappers标簽的屬性到Configuration中
        mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
        throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
}
複制代碼           

最後parse() 方法會傳回Configuration,SqlSessionFactoryBuilder會基于Configuration建立DefaultSqlSessionFactory并傳回,如下所示。

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}
複制代碼           

DefaultSqlSessionFactory類圖如下所示。

淺析MyBatis的配置加載流程

至此,讀取配置檔案mybatis-config.xml和建構SqlSessionFactory的基本原理已經介紹完畢。

總結

本篇文章對MyBatis讀取配置檔案并建構SqlSessionFactory的一個整體流程進行了介紹,可以概括如下。

  1. Resources工具類擷取配置檔案輸入字元流或位元組流;
  2. 建立SqlSessionFactoryBuilder;
  3. SqlSessionFactoryBuilder中會建立XMLConfigBuilder來解析配置檔案得到全局唯一的Configuration;
  4. 基于Configuration建立DefaultSqlSessionFactory。

實際上,關于MyBatis配置檔案的讀取,最關鍵的部分在于如何注冊映射檔案/映射接口,該部分内容,會在後面的文章中進行學習。

連結:https://juejin.cn/post/7203690731277697080

繼續閱讀