ApplicationContext refresh 過程及一些重要的 processor 解析

回顧

上文 其實咱們已經實現了一個簡單的 BeanFactory 它具的功能有html

  • 註冊 Bean 到容器,經過限定名獲取 Bean
  • 能夠攔截 Bean 初始化先後的處理
  • 能夠在 Bean 屬性注入後和即將銷燬時作一些邏輯處理
  • 解決了循環依賴

其實總結起來它實現的方法就是:加載 Bean 定義、實例化 Bean,很簡單吧java

Bean 在 spring 中的完整生命週期,能夠自行查看spring 的 BeanFactory 接口,它在最上面的註釋有詳細說明。git

但實際的應用場景除了這個主要 Bean 管理外,還有一些消息廣播、國際化、事件監聽等spring

本文內容

本文想分析下 ApplicationContextrefresh 過程,先來個總結性的過程,以便後續一堆枯燥的源碼分析數據庫

  • 初始階段:準備工做springboot

  • 第一步,咱們先和獲得一個 BeanFactory ,這裏初始化的是 DefaultListableBeanFactorymybatis

  • 第二步,咱們須要配置這個 BeanFactory 像 spel 表達式解析器、Environment 都要配置到工廠裏面,以便後續的處理app

  • 第三步,咱們容許添加 BeanFactoryProcessor 來修改 BeanFactory 中的 Bean ,經常使用的子類 BeanDefinitionRegistryPostProcessor 它能夠往容器中添加 Bean 定義框架

    像 @Import、@PropertySource、@ComponentScan、@ImportResource、@Bean methods 還有 tk.mybatis 都是靠它來注入自定義的 Bean 定義 的,不清楚什麼是 bean 定義看個人 上篇文章maven

  • 第五步,固然是 回調 上面收集到的全部的 BeanFactoryProcessor

  • 第六步,回調 全部收集到的 BeanPostProcessor,這些 processor 是已經加到容器中的,若是你看到這裏,不懂什麼是 BeanPostProcessor 請回頭看 上文 的 bean 的生命週期,不清楚回調那就沒辦法了,你還不夠經驗看這篇文章

  • 第七步,國際化初始化(對於主體來講,這算是支線,由於大部分狀況下咱們都不是寫的國際項目)

  • 第八步,初始化事件廣播器。事件:常見的如頁面加載完成有 onLoad 事件,應用剛啓動有 onLaunch 事件,項目中用得很少,多是 spring 內部用得多吧

  • 第九步,留一個回調給子類來初始化特別的 bean ,默認空的

  • 第十步,註冊監聽器(全部實現了 ApplicationListener 的類),並把以前的一些事件發佈一下

  • 第十一步,基本上是單例實例化,基本上咱們項目中的類都是在這個方法中實例化的

  • 結束階段:清理工做

看這個過程的好處

  • 熟悉 IOC 容器初始化的流程,能夠把 spring 用得更好
  • 一些像 spel 工具類性質的東西能夠直接拿過來用,不必引第三方庫或重複造輪子

正文

不會把每一步都細品,只會對一些關鍵的步驟進行解讀,像如何加載 Bean 定義,讀 xml 和讀註解的,讀者能夠自行研讀,本文討論幾個關鍵的 processor

咱們都知道,Bean 的生命週期中,能夠添加 BeanPostProcessor 在 bean 的初始化前和初始化後作一些處理,一樣的在 BeanFactory 也有一個 BeanFactoryPostProcessor 容許你在 BeanFactory 初始化後,修改 BeanFactory。

樣例:

咱們看一下 PropertyPlaceholderConfigurer xml 配置時代的產物, 它的繼承結構以下

PropertyResourceConfigurer implements BeanFactoryPostProcessor
   |-PlaceholderConfigurerSupport
      |-PropertyPlaceholderConfigurer

咱們一般會在最開始配置這個

<context:property-placeholder location="classpath:jdbc.properties" />

PropertyPlaceholderConfigurer用於對項目中的 @Value 值進行處理,把值注入進屬性中,下面看下它如何實現的

查看關鍵方法 PropertyResourceConfigurer.postProcessBeanFactory,分爲三步

Properties mergedProps = mergeProperties();     // 從 location 中加載屬性

// Convert the merged properties, if necessary.
convertProperties(mergedProps);                 //什麼都沒幹,一個模板方法,可用於密碼加密,自定義屬性轉換

// Let the subclass process the properties.
processProperties(beanFactory, mergedProps);    // 真正處理屬性的地方,解析 spel 表達式 ${}

真正的處理過程使用了訪問者模式在 BeanDefinitionVisitor 中。

springboot 又是怎麼處理的呢,springboot 是固定了配置文件 application.properties ,而再也不使用 location 來配置路徑,它使用了 PropertySourcesPlaceholderConfigurer 來解析配置 ,它一樣繼承自 PlaceholderConfigurerSupport 而且重寫了 postProcessBeanFactory 關鍵方法,在最後面使用了

PropertySourcesPropertyResolver 進行了屬性解析。

使用方式能夠在 這篇文章 看到

BeanDefinitionRegistryPostProcessor 繼承自 BeanFactoryPostProcessor 它的參數是 BeanDefinitionRegistry 一個 Bean 定義 的註冊器,它能夠作些什麼呢 ,查看它的方法,能夠對 bean 定義作增刪改查,厲害吧。

registerBeanDefinition
removeBeanDefinition
getBeanDefinition
containsBeanDefinition
getBeanDefinitionNames
getBeanDefinitionCount
isBeanNameInUse

樣例一:

ConfigurationClassPostProcessor 中,它實現了 BeanDefinitionRegistryPostProcessor ,它使用 ConfigurationClassParser 來解析項目中配置的 @Configuration 類,而後使用 ConfigurationClassBeanDefinitionReader 把解析到的類加載到 Bean 定義

具體細節查看 這篇文章

樣例二:

在 tkmybatis 的 MapperScannerConfigurer 中,也實現了 BeanDefinitionRegistryPostProcessor ,它使用 ClassPathMapperScanner 來掃描 Mapper 類,添加進容器

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner

劃重點

其實總結來講,refresh 的過程只是給你提供了一個大體的執行框架,具體的處理仍是在一些 processor,listener 中,網上有大部分源碼解讀都拿這個 refresh 過程作大篇幅的講解,其實沒多大必要,就像 servlet 的生命週期同樣,瞭解其執行過程,但剩下的處理都是子類去具現化的。

分享一篇說得不錯的文章

BeanPostProcessor和BeanFactoryPostProcessor淺析以及在spring初始化中回調

一點小推廣

創做不易,但願能夠支持下個人開源軟件,及個人小工具,歡迎來 gitee 點星,fork ,提 bug 。

Excel 通用導入導出,支持 Excel 公式
博客地址:https://blog.csdn.net/sanri1993/article/details/100601578
gitee:https://gitee.com/sanri/sanri-excel-poi

使用模板代碼 ,從數據庫生成代碼 ,及一些項目中常常能夠用到的小工具
博客地址:https://blog.csdn.net/sanri1993/article/details/98664034
gitee:https://gitee.com/sanri/sanri-tools-maven

相關文章
相關標籤/搜索