天天看點

Spring源碼深度解析 整合MyBatis總結

MyBatis 獨立使用

建立 PO

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

建立 Mapper

Spring源碼深度解析 整合MyBatis總結

建立配置檔案

配置檔案的基本結構

Spring源碼深度解析 整合MyBatis總結

configuration :根元素

properties 定義配置外在化

settings 一些全局性的配置

typeAliases :為一些類定義别名

typeHandlers :定義類型處理,也就是定義 Java 類型與資料庫中的資料類型之間的轉

換關系

objectFactory :用于指定結果集對象的執行個體是如何建立的

plugins: MyBatis 的插件,插件可以修改 MyBatis 内部的運作規則

environments 環境

environment 配置 MyBatis 的環境

transactionManager :事務管理器

dataSource 資料源

mappers 指定映射檔案或映射類

Spring源碼深度解析 整合MyBatis總結

建立映射檔案

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

建立測試類

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

Spring 整合 MyBatis

Spring 配置檔案

Spring源碼深度解析 整合MyBatis總結

MyBatis 配置檔案

Spring源碼深度解析 整合MyBatis總結

映射檔案(保持不變)

Spring源碼深度解析 整合MyBatis總結

測試

Spring源碼深度解析 整合MyBatis總結

源碼分析

sqlSessionFactory建立

對于配置檔案的讀取解析, Spring 應該通過 org.mybatis.Spring.

SqISessionFactoryBean 封裝了 MyBatis 中的實作 這個類的層次結構

Spring源碼深度解析 整合MyBatis總結

InitializingBean 實作此接口的 bean 會在初始化時調用其 afterPropertiesSet 方法來進

行 bean 的邏輯初始化

FactoryBean :一旦某個 bean 實作次接 口,那麼通過 getBean 方法擷取 bean 時其 實是

擷取此類的 getObject()傳回的 執行個體

SqlSessionFactoryBean 的初始化

Spring源碼深度解析 整合MyBatis總結

此函數主要目的就是對于 sqlSessionFactory 的初始化,SqlSessionFactory 是所有 MyBatis 功能的基礎

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

從函數中可以看到 ,盡管我們還是習慣于将 MyBatis 的配置與 Spring 的配置獨立出來

但是,這并不代表 Spring 中的配置不支援直接配置。 也就是說,在上面提供的示例中,完全

可以取消配置中的 configLocation 屬性 ,而把其中的屬性直接寫在 SqlSessionFactoryBean 中

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

如果隻按照常用的配置,那麼我們隻需要在函數最開始按照如下方式處理

configuration:

Spring源碼深度解析 整合MyBatis總結

擷取 SqlSessionFactoryBean 執行個體

Spring源碼深度解析 整合MyBatis總結

MapperFactoryBean 的建立

對于單獨使用 MyBatis 的時候調用資料庫接口的方式是:

Spring源碼深度解析 整合MyBatis總結

而對于 Spring 的建立方式

Spring源碼深度解析 整合MyBatis總結

Spring 中擷取的名為 userMapper 的 bean 其實是與單獨使用 MyBatis 完成了一樣的功

能,那麼可以推斷,在 bean 的建立過程中一定是使用了 MyBatis 中的原生方法

sq!Sessi on. getMapper(U serMapper.class)進行了再一次封裝 結合配置檔案,我們把分析目标轉向

org.mybatis.Spring.mapper. MapperFactoryBean ,初步推測其中的邏輯應該在此類中實作 檢視類的層次結構圖 MapperFactoryBean

Spring源碼深度解析 整合MyBatis總結

MapperFactoryBean 的初始化

Spring源碼深度解析 整合MyBatis總結

MapperFactoryBean 的初始化包括對 DAO 配置的驗證以

及對 DAO 的初始工作,其中 initDao ()方法是模闆方法 ,設計為留給子類做進一步邏輯處理

而 checkDaoConfig()才是重點

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

super.check.DaoConfig()在 SqlSessionDaoSupport 類中實作,代碼如下:

Spring源碼深度解析 整合MyBatis總結

Spring 做了以下幾個方面的工作

父類中對于 sqlSession 不為空的驗證

sqlSession 作為根據接口建立映射器代理的接觸類一定不可以為空 ,而 sqISession 的初始化

工作是在設定其 sqlSessionFactory 屬性時完成的

Spring源碼深度解析 整合MyBatis總結

也就是說,對于下面的配置如果忽略了對于 sqlSessionFactory 屬性的設定,那麼在此時就

會被檢測出來

Spring源碼深度解析 整合MyBatis總結

映射接口的驗證

接口是映射器的基礎, sqISession 會根據接口動态建立相應的代理類,是以接口必不可少

映射檔案存在性驗證

擷取 MapperFactoryBean 的執行個體

由于 MapperFactoryBean 實作了 FactoryBean 接口, 是以當通過 getBean 方法擷取對應執行個體

的時候其實是擷取該類的 getObject()函數傳回的執行個體

Spring源碼深度解析 整合MyBatis總結

MapperScannerConfigurer

在 applicationContext.xml 中配置了 userMapper 供需要時使用 但如果需要用到的映射器

較多的話,采用這種配置方式就顯得很低效 為了解決這個問題,我們可以使用 MapperScanner

Configurer ,讓它掃描特定的包,自動幫我們成批地建立映射器 這樣一來 ,就能大大減少配

置的工作量

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

通過上面的配置, Spring 就會幫助我們對 test.mybatis.dao 下面的所有接口進行自動的注入,

而不需要為每個接口重複在 Spring 配置檔案中進行聲明了 那麼,這個功能又是如何做到的呢

MapperScannerConfigurer 中又有哪些核心操作呢 首先檢視類的層次結構圖

Spring源碼深度解析 整合MyBatis總結

查找類的 afterPropettiesSet 方法來看看類的初始化邏輯

Spring源碼深度解析 整合MyBatis總結

afterPropertiesSet()方法除了一句對 basePackage屬性的驗證代碼外并沒有太多的邏輯實作

再次檢視 MapperScannerConfigurer 類層次結構圖中感興趣的接口 ,發現了 BeanDefinitionRegistryPostProcessor

與 BeanFactoryPostProcessor, Spring 在初始化的過程中同樣會保證這兩個接口的調用

首先檢視 MapperScannerConfigurer 類中對于 BeanFactoryPostProcessor 接口的實作:

Spring源碼深度解析 整合MyBatis總結

沒有任何邏輯實作

檢視MapperScannerConfigurer類中對于 BeanDefinitionRegistryPostProcessor 接口的實作

Spring源碼深度解析 整合MyBatis總結

processPropertyPlaceHolders 屬性的處理

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

BeanDefinitionRegistries 會在應用啟動的時候調用,并且會早于 BeanFactory PostProcessors 的調

用,這就意味着 PropertyResourceConfigurers 還沒有被加載所有對于屬性檔案的引用将會失效

為避免此種情況發生,此方法手動地找出定義的 PropertyResourceConfigurers 并進行提前調用

以保證對于屬性的引用可以正常工作

如要建立配置檔案如 test.properties 并添加屬性對

Spring源碼深度解析 整合MyBatis總結

然後在 Spring 配置檔案中加入屬性檔案解析器:

Spring源碼深度解析 整合MyBatis總結

修改 MapperScannerConfigurer 類型的 bean 的定義

Spring源碼深度解析 整合MyBatis總結

這個配置并沒有達到預期的效果,因為在解析${basePackage }的時候

PropertyPlaceholderConfigurer 還沒有被調用,也就是屬性檔案中的屬性還沒有加載至記憶體中,

Spring 還不能直接使用它 為了解決這個問題, Spring 提供了 processPropertyPlaceHolders 屬性,

需要這樣配置 MapperScannerConfigurer 類型的 bean

Spring源碼深度解析 整合MyBatis總結

通過 processPropertyPiaceHolders 屬性的配置,将程式引人我們正在分析的 processProperty

PlaceHolders 函數中來完成屬性檔案的加載。這個函數所做的事情

找到所有已經注冊的 PropertyResourceConfigurer 類型的 bean

模拟 Spring 中的環境來用處理器 這裡通過使用 new DefaultListableBeanFactory ()來模

模 Spring 中的環境(完成處理器的調用後便失效),将映射的 bean ,也就是 MapperScanner

Configurer 類型 bean 注冊到環境中來進行後理器的調用,處理器 PropertyPlaceholderConfigurer

調用完成的功能,即找出所有 bean 中應用屬性檔案的變量并替換。 也就是說,在處理器調用

後,模拟環境中模拟的 MapperScannerConfigurer 類型的 bean 如果有引人屬性檔案中的屬性那

麼已經被替換了,這時,再将模拟 bean 中相關的屬性提取出來應用在真實的 bean 中

根據配置屬性生成過濾器

屬性設定後通過在 scanner.registerFilters()代碼中生成對應的過濾器來控制掃描結果

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

根據之前屬性的配置生成了對應的過濾器

annotationClass 屬性處理

markerlnterface 屬性處理

全局預設處理

package-info.java 處理

控制掃描檔案 Spring 通過不同的過濾器完成,這些定義的過濾器

記錄在了 includeFilters 和 excludeFilters 屬性中

Spring源碼深度解析 整合MyBatis總結

掃描 Java 檔案

設定了相關屬性以及生成了對應的過濾器後便可以進行檔案的掃描了,掃描工作是由

ClassPath.MapperScanner 類型的執行個體 scanner 中的 scan 方法完成的

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

scan 是個全局方法,掃描工作通過 doScan(basePackages)委托給了 doScan 方法,同時,還包括

了 includeAnnotationConfig 屬性的處理, AnnotationConfigUtils.registerAnnotation ConfigProcessors

(this.registry)代碼主要是完成對于注解處理器的簡單注冊,比如 AutowiredAnnotationBeanPost

Processor RequiredAnnotationBeanPostProcessor 等,重點研究檔案掃描功能的實作

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

Spring 中對于自動掃描的注冊 聲明 MapperScannerConfigurer 類型的 bean 目的是不需要我們對于每個接口都注

冊一個 MapperFactoryBean 類型的對應的 bean ,但是,不在配置檔案中注冊并不代表這個 bean

不存在,而是在掃描的過程中通過編碼的方式動态注冊。實作過程在上面的函數中可以看得非常清楚

Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結
Spring源碼深度解析 整合MyBatis總結

findCandidateComponents 方法根據傳人的包路徑資訊并結合類檔案路徑拼接成檔案的絕

對路徑,同時完成了檔案的掃描過程并且根據對應的檔案生成了對應的 bean ,使用

ScannedGenericBeanDefinition 類型的 bean 承載資訊, bean 中隻記錄了 resource 和 source 資訊

這裡,更感興趣的是 isCandidateComponent(metadataReader),此句代碼用于判斷目前掃描

的檔案是否符合要求,而之前注冊的一些過濾器資訊也正是在此時派上用場的

Spring源碼深度解析 整合MyBatis總結

看到了之前加入過濾器的兩個屬性 excludeFilters 和 includeFilters ,井且知道對應的文

件是否符合要求是根據過濾器中的 match 方法所傳回的資訊來判斷的,當然使用者可以實作并注

冊滿足自己業務邏輯的過濾器來控制掃描的結果, metadataReader 中有你過濾所需要的全部文

件資訊 至此,完成了檔案的掃描過程的分析