前言
在原始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() 方法可以分為三類,概括如下。
- 基于配置檔案字元流建構SqlSessionFactory;
- 基于配置檔案位元組流建構SqlSessionFactory;
- 基于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的類圖,如下所示。
通過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-config.xml和建構SqlSessionFactory的基本原理已經介紹完畢。
總結
本篇文章對MyBatis讀取配置檔案并建構SqlSessionFactory的一個整體流程進行了介紹,可以概括如下。
- Resources工具類擷取配置檔案輸入字元流或位元組流;
- 建立SqlSessionFactoryBuilder;
- SqlSessionFactoryBuilder中會建立XMLConfigBuilder來解析配置檔案得到全局唯一的Configuration;
- 基于Configuration建立DefaultSqlSessionFactory。
實際上,關于MyBatis配置檔案的讀取,最關鍵的部分在于如何注冊映射檔案/映射接口,該部分内容,會在後面的文章中進行學習。
連結:https://juejin.cn/post/7203690731277697080