1. 程式人生 > >精盡MyBatis原始碼分析 - Spring-Boot-Starter 原始碼分析

精盡MyBatis原始碼分析 - Spring-Boot-Starter 原始碼分析

> 該系列文件是本人在學習 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 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 兩個類(感興趣的可以去看看,我沒搞懂這兩個類