精盡MyBatis原始碼分析 - Spring-Boot-Starter 原始碼分析
阿新 • • 發佈:2020-11-28
> 該系列文件是本人在學習 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-Spring原始碼分析》**](https://www.cnblogs.com/lifullmoon/p/14015235.html)文件中對 Spring 整合 MyBatis 的方案進行了分析,`MyBatis-Spring` 讓你能夠在 Spring 專案中方便地使用 MyBatis,隨著 **Spring Boot** 框架受到業界的廣泛關注,有越來越多企業使將它使用到正式的生產環境,它支援整合其他元件,讓你能夠在 Spring Boot 專案中更加方便地使用其他元件
當然,MyBatis 也提供了整合到 Spring Boot 的方案 `Spring-Boot-Starter`,能夠讓你快速的在 Spring Boot 上面使用 MyBatis,那麼我們來看看這個 [Spring-Boot-Starter 子專案](http://mybatis.org/spring-boot-starter) 是如何將 MyBatis 整合到 Spring 中的
在開始讀這篇文件之前,需要對 Spring 有一定的瞭解,其中`Spring-Boot-Starter` 基於 `MyBatis-Spring` 來實現的,所以可以先檢視我的另一篇[**《MyBatis-Spring原始碼分析》**](https://www.cnblogs.com/lifullmoon/p/14015235.html)文件來了解 `MyBatis-Spring`,本文可以結合我的原始碼註釋([Spring-Boot-Starter 原始碼分析 GitHub 地址](https://github.com/liu844869663/spring-boot-starter))進行閱讀
## 簡述
MyBatis 的 `Spring-Boot-Starter` 子專案我們主要看到兩個模組
- **mybatis-spring-boot-starter**:定義了一個 pom 檔案,引入 MyBatis 相關依賴
- **mybatis-spring-boot-autoconfigure**:MyBatis 整合到 Spring Boot 的具體實現
主要涉及到的幾個類:
- `org.mybatis.spring.boot.autoconfigure.MybatisProperties`:MyBatis 的配置類,注入 Spring Boot 的配置檔案中 MyBatis 的相關配置
- `org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration`:實現 InitializingBean 介面,MyBatis 自動配置類,用於初始化 MyBatis,核心類
大致邏輯如下:
通過 `MybatisAutoConfiguration` 這個自動配置類,再加上 `MybatisProperties` 的配置資訊,生成 SqlSessionFactory 和 SqlSessionTemplate 類,完成初始化,通過 @MapperScan 註解指定 Mapper 介面
## 配置示例
```yaml
mybatis:
type-aliases-package: tk.mybatis.simple.model
mapper-locations: classpath:mapper/*.xml
config-location: classpath:mybatis-config.xml
```
在`application.yml`中新增上面三個MyBatis的相關配置即可,然後在啟動類上面新增`@MapperScan`註解指定 Mapper 介面所在包路徑即可
注意:你還需要定義一個 DataSource 資料來源,可選 `Druid`、`HikariCP`等資料庫連線池,這裡就不講述如何使用了
## MybatisProperties
`org.mybatis.spring.boot.autoconfigure.MybatisProperties`:MyBatis 的配置類,通過 Spring Boot 中的 `@ConfigurationProperties` 註解,注入 MyBatis 的相關配置,程式碼如下:
```java
@ConfigurationProperties(prefix = MybatisProperties.MYBATIS_PREFIX)
public class MybatisProperties {
public static final String MYBATIS_PREFIX = "mybatis";
private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
/**
* Location of MyBatis xml config file.
* mybatis-config.xml 配置檔案的路徑
*/
private String configLocation;
/**
* Locations of MyBatis mapper files.
* XML 對映檔案的路徑
*/
private String[] mapperLocations;
/**
* Packages to search type aliases. (Package delimiters are ",; \t\n")
* 需要設定別名的包路徑
*/
private String typeAliasesPackage;
/**
* The super class for filtering type alias. If this not specifies, the MyBatis deal as type alias all classes that
* searched from typeAliasesPackage.
*/
private Class> typeAliasesSuperType;
/**
* Packages to search for type handlers. (Package delimiters are ",; \t\n")
*/
private String typeHandlersPackage;
/**
* Indicates whether perform presence check of the MyBatis xml config file.
*/
private boolean checkConfigLocation = false;
/**
* Execution mode for {@link org.mybatis.spring.SqlSessionTemplate}.
*/
private ExecutorType executorType;
/**
* The default scripting language driver class. (Available when use together with mybatis-spring 2.0.2+)
*/
private Class extends LanguageDriver> defaultScriptingLanguageDriver;
/**
* Externalized properties for MyBatis configuration.
*/
private Properties configurationProperties;
/**
* A Configuration object for customize default settings. If {@link #configLocation} is specified, this property is
* not used.
*/
@NestedConfigurationProperty
private Configuration configuration;
/**
* 獲取 XML 對映檔案路徑下的資源物件
*
* @return Resource 資源陣列
*/
public Resource[] resolveMapperLocations() {
return Stream.of(Optional.ofNullable(this.mapperLocations).orElse(new String[0]))
.flatMap(location -> Stream.of(getResources(location))).toArray(Resource[]::new);
}
/**
* 獲取某個路徑下的資源
*
* @param location 路徑
* @return Resource 資源陣列
*/
private Resource[] getResources(String location) {
try {
return resourceResolver.getResources(location);
} catch (IOException e) {
return new Resource[0];
}
}
}
```
- `configLocation`:mybatis-config.xml 配置檔案的路徑
- `mapperLocations`:XML 對映檔案的路徑
- `typeAliasesPackage`:需要設定別名的包路徑,多個以`, ; \t\n`分隔
- `typeAliasesSuperType`:需要設定別名的父 Class 型別
- `typeHandlersPackage`:型別處理器的包路徑
- `checkConfigLocation`:檢查 mybatis-config.xml 配置檔案是否存在
- `executorType`:Executor 執行器型別,預設 SIMPLE
- `defaultScriptingLanguageDriver`:設定預設的 LanguageDriver 語言驅動類,預設為 XMLLanguageDriver
其中定義了字首為`mybatis`,說明你可以在 Spring Boot 專案中的 application.yml 配置檔案中,以該字首定義 MyBatis 的相關屬性
我們通常新增前面三個配置就可以了
這裡注意到僅添加了 @ConfigurationProperties 註解,在作為 Spring Bean 注入到 Spring 容器中時,會將相關配置注入到屬性中,但是這個註解不會將該類作為 Spring Bean 進行注入,需要結合 @Configuration 註解或者其他註解一起使用
## SpringBootVFS
`org.mybatis.spring.boot.autoconfigure.SpringBootVFS`:MyBatis 需要使用到的虛擬檔案系統,用於替代 MyBatis 的 `org.apache.ibatis.io.DefaultVFS` 預設類
使用 Spring Boot 提供的 PathMatchingResourcePatternResolver 解析器,獲取到指定路徑下的 Resource 資源,程式碼如下:
```java
public class SpringBootVFS extends VFS {
private final ResourcePatternResolver resourceResolver;
public SpringBootVFS() {
this.resourceResolver = new PathMatchingResourcePatternResolver(getClass().getClassLoader());
}
@Override
public boolean isValid() {
return true;
}
@Override
protected List list(URL url, String path) throws IOException {
String urlString = url.toString();
String baseUrlString = urlString.endsWith("/") ? urlString : urlString.concat("/");
Resource[] resources = resourceResolver.getResources(baseUrlString + "**/*.class");
return Stream.of(resources).map(resource -> preserveSubpackageName(baseUrlString, resource, path))
.collect(Collectors.toList());
}
private static String preserveSubpackageName(final String baseUrlString, final Resource resource,
final String rootPath) {
try {
return rootPath + (rootPath.endsWith("/") ? "" : "/")
+ resource.getURL().toString().substring(baseUrlString.length());
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
```
## MybatisAutoConfiguration
`org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration`:實現 InitializingBean 介面,MyBatis 自動配置類,用於初始化 MyBatis,核心類
### 構造方法
```java
@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
public class MybatisAutoConfiguration implements InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(MybatisAutoConfiguration.class);
/**
* MyBatis 配置資訊
*/
private final MybatisProperties properties;
private final Interceptor[] interceptors;
private final TypeHandler[] typeHandlers;
private final LanguageDriver[] languageDrivers;
private final ResourceLoader resourceLoader;
private final DatabaseIdProvider databaseIdProvider;
private final List configurationCustomizers;
public MybatisAutoConfiguration(MybatisProperties properties, ObjectProvider interceptorsProvider,
ObjectProvider typeHandlersProvider, ObjectProvider languageDriversProvider,
ResourceLoader resourceLoader, ObjectProvider databaseIdProvider,
ObjectProvider
- > configurationCustomizersProvider) {
this.properties = properties;
this.interceptors = interceptorsProvider.getIfAvailable();
this.typeHandlers = typeHandlersProvider.getIfAvailable();
this.languageDrivers = languageDriversProvider.getIfAvailable();
this.resourceLoader = resourceLoader;
this.databaseIdProvider = databaseIdProvider.getIfAvailable();
this.configurationCustomizers = configurationCustomizersProvider.getIfAvailable();
}
}
```
我們主要來看到類上面定義的幾個註解:
- `@Configuration`:可以當作一個 Spring Bean 注入到 Spring 上下文中
- `@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })`
保證存在 value 中所有的 Class 物件,以確保可以建立它們的例項物件,這裡就保證 SqlSessionFactory 和 SqlSessionFactoryBean 都能夠被建立
- `@ConditionalOnSingleCandidate(DataSource.class)`
保證存在 value 型別對應的 Bean,這裡確保已經存在一個 DataSource 資料來源物件
- `@EnableConfigurationProperties(MybatisProperties.class)`
注入 value 中所有的型別的 Bean,這裡會讓 MybatisProperties 作為 Spring Bean 注入到 Spring 上下文中
- `@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })`
在載入 value 中的所有類之後注入當前 Bean,會先注入 DataSourceAutoConfiguration 和 MybatisLanguageDriverAutoConfiguration 兩個類(感興趣的可以去看看,我沒搞懂這兩個類