spring-boot 之深入理解 ApplicationContext

spring-boot 之深入理解 ApplicationContext

Spring核心:IoC容器

我們想要理解 Application Context ,首先得知道這個東西是用來解決什麼的,我們知道Spring framework的核心是IoC容器,而Spring主要提供了兩個容器系列,一個是實現BeanFactory接口的簡單容器系列,其主要實現了容器最基本的功能;另一個就是 ApplicationContext 應用上下文,其在簡單容器的基礎上,增加了好多面向框架的特性,同時對應用環境進行了適配。

有了以上的認知後,我們來看IoC容器最基本的接口定義 BeanFactory ,BeanFactory 就是對於IoC容器從Client視角來看做的抽象,既然有了IoC容器,那容器管理的是什麼呢?容器管理的是對象(Bean),我們需要對被管理的對象進行抽象,這就有了 BeanDefinition。

通過上面的講述,我們已經有了幾個基本的數據結構抽象了:

  • BeanFactory:對IoC容器的client端抽象

  • BeanDefinition:對IoC管理的對象的數據抽象

  • ApplicationContext:在 BeanFactory 基礎上增加了面向框架的特性

我們再來看一個稍微複雜點的類圖:

spring-boot 之深入理解 ApplicationContext

我們來簡要說下上面的圖,幫助理解。

  • 第一條線,從 BeanFactory 到 HierarchicalBeanFactory 再到 ConfigurableBeanFactory ,在這邊主線上,BeanFactory 定義了IoC容器最基本的接口,如getBean,然後 HierarchicalBeanFactory 新增了了getParentBeanFactory接口,實現了在當前容器中找到不到Bean,可以遞歸查找父容器的功能,最後 ConfigurableBeanFactory 提供了配置BeanFactory的接口。

  • 第二條線,從 BeanFactory 到 ListableBeanFactory 再到 ApplicationContext ,這條線主要是在 ApplicationContext 接口中繼承了 MessageSource, ApplicationEventPublisher, ResourcePatternResolver 等接口,在 BeanFactory 基礎上新增了好多新功能。

BeanFactory

介紹完上面的類圖後,我們下面具體來介紹 BeanFactory 和 ApplicationContext,先是 BeanFactory。

BeanFactory作為基礎的IoC容器,管理了spring所有的Bean,提供了最基本的容器功能,但是BeanFactory是一個接口,現在默認的可使用實現是 DefaultListableBeanFactory, 我們來看下如何使用 DefaultListableBeanFactory 來實現Bean的註冊:

spring-boot 之深入理解 ApplicationContext

此處配合 AnnotatedBeanDefinitionReader 完成了Bean的註冊。

ApplicationContext

ApplicationContext 是一個高級形態的 IoC 容器,ApplicationContext 在 BeanFactory 基礎上附加了好多其他功能:

spring-boot 之深入理解 ApplicationContext

  • MessageSource:支持不同的信息源,支持國際化實現

  • ResourcePatternResolver:訪問資源,使得我們可以從不同的地方獲取到Bean定義

  • ApplicationEventPublisher:提供事件機制,跟Bean的整個生命週期結合,方便擴展

以上是一些 ApplicationContext 提供的額外功能,正是這些功能使得 ApplicationContext 與 BeanFactory 相比,ApplicationContext 提供了更多面向框架的功能,因此我們一般使用 ApplicationContext 進行開發。

一個最簡單的 ApplicationContext 的實現是 StaticApplicationContext,主要是為了測試而用,不應該在生產環境中使用,一個簡單的使用示例:

spring-boot 之深入理解 ApplicationContext

其中重點是 refresh 方法,會涉及到IoC容器啟動的一系列複雜操作,下面我們來具體分析下。

在具體介紹 refresh 之前,我們得明確 IoC 容器的最主要功能是Bean管理,那我們就必須要經歷:

  1. 找到這些Bean定義

  2. 加載Bean定義

  3. 創建Bean

那我們就跟著上面的3步驟來看下refresh中是如何實現的。


StaticApplicationContext 自編碼實現Bean定義、註冊、創建

StaticApplicationContext 中不支持從外部配置文件中讀取Bean,只支持編程的方式來註冊Bean,我們來看下如何做:

spring-boot 之深入理解 ApplicationContext

spring-boot 之深入理解 ApplicationContext

上面的代碼中我們定義了BeanContext來註冊BeanDefinition,最後通過refresh來更新。

來看測試例子:

spring-boot 之深入理解 ApplicationContext

好了我們知道了簡單的使用規則後,我們再來看看一個稍微複雜點的 ApplicationContext:AnnotationConfigApplicationContext,這個我們就可以完整的看到Bean3 部曲:尋找,載入,創建。我們先來看尋找。


BeanDefinition 的 Resource 定位

第一個尋找其實是Resource定位過程,spring將IO訪問都抽象為Resource的訪問,在 AnnotationConfigApplicationContext 中,將 Resource 定位委託給了 ClassPathBeanDefinitionScanner ,下面我們看下主要的類圖:

spring-boot 之深入理解 ApplicationContext

其中

  • ResourcePatternResolver 用來從classpath中加載Resource;

  • MetadataReaderFactory 用來根據Resource生成MetadataReader;

  • BeanNameGenerator用來生成掃描到的Bean在容器中的名字;

  • ScopeMetadataResolver則用來處理掃描到的Bean的Scope。

我們先來看第一階段Resource定位過程,此處Resource的定位是由ResourcePatternResolver來實現的,關於Resource相關的可以看文章Attack-spring-boot-3-1: 統一資源定位

在 AnnotationConfigApplicationContext 中,我們通過 scan 方法來尋找候選的Bean,跟蹤代碼我們會發現 scan 最終是調用到了 ResourcePatternResolver 來加載進 Resource, 關於資源定位,我們來看一個類圖:

spring-boot 之深入理解 ApplicationContext

  • AbstractApplicationContext 繼承了 DefaultResourceLoader,getResource(String)直接用DefaultResourceLoader的了

  • AbstractApplicationContext類的內 部聲明有一個resourcePatternResolver,類型是 ResourcePatternResolver,對應的實例類型為 PathMatchingResourcePatternResolver

  • PathMatchingResourcePatternResolver構造的時候會接受一個ResourceLoader,而AbstractApplicationContext本身又繼承自 DefaultResourceLoader,當然就直接把自身傳遞進去

  • ApplicationContext的實現類在作為ResourceLoader或者 ResourcePatternResolver時候的行為, 完全就是委派給了 PathMatchingResourcePatternResolver和DefaultResourceLoader來做。


BeanDefinition 註冊

第二步驟是Bean的註冊,也是在scan中完成:

spring-boot 之深入理解 ApplicationContext


Bean 創建

前面Bean的定位和註冊都在scan中完成了,現在終於到了refresh函數了,我們來看下refresh中做了啥,根據前面的講述,refresh中應該是開始對Bean進行創建了,實際上我們會發現 refresh 中還是會做對Bean的註冊。

spring-boot 之深入理解 ApplicationContext

上面是整個refresh過程的概括,分別說下每步做什麼。


prepareBeanFactory 標準初始化配置 BeanFactory

設置 BeanFactory 工廠創建 Bean 時使用什麼樣的類加載器,默認情況下使用線程上下文類加載器(默認為 AppClassLoader),添加 BeanPostProcessor,此處包含:ApplicationContextAwareProcessor 和 ApplicationListenerDetector。


postProcessBeanFactory 對 BeanFactory 進行個性化定製的擴展

這時候加載了 Bean 的定義,但是這時候還沒有 Bean 被實例化,允許註冊一些 BeanPostProcessors 類型的 Bean 用來在 Bean 初始化前後做一些事情。

例如 AbstractRefreshableWebApplicationContext 中就註冊了 ServletContextAwareProcessor ,用來把 servletContext 設置到實現了 ServletContextAware 接口的 Bean。

spring-boot 之深入理解 ApplicationContext


invokeBeanFactoryPostProcessors 執行 BeanFactoryPostProcessor 來對 BeanFactory 進行擴展

通過委託給 PostProcessorRegistrationDelegate 來執行所有的 BeanFactoryPostProcessor。

此處先介紹兩個接口 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor:

spring-boot 之深入理解 ApplicationContext

通過執行 BeanFactoryPostProcessor ,來對 BeanFactory 中的 Bean 定義進行修改,比如常見的是統一設置某些 Bean 的屬性變量值, 對指定的 Bean 定義進行修改或者註冊新的 Bean。

spring-boot 之深入理解 ApplicationContext

BeanDefinitionRegistryPostProcessor 接口繼承自 BeanFactoryPostProcessor,它新添加了一個接口,用來在BeanFactoryPostProcessor 實現類中 postProcessBeanFactory 方法執行前再註冊一些 Bean 到 beanFactory 中。

  • 在 invokeBeanFactoryPostProcessors 中首先執行實現了 BeanDefinitionRegistryPostProcessor 接口的 Bean 的 postProcessBeanDefinitionRegistry 方法

  • 然後再執行實現了 BeanFactoryPostProcessor 接口的 Bean 的 postProcessBeanFactory 方法

  • 由於接口的實現類可能會有多個,可以通過實現 PriorityOrdered 或者 Ordered 接口給每個Bean定義一個優先級,規定 PriorityOrdered 接口的優先級大於實現 Ordered 的優先級。

在 AnnotationConfigApplicationContext 中,會通過 AnnotatedBeanDefinitionReader 將相關的 BeanPostProcessor 預先註冊到BeanFactory中,這其中就包括了 ConfigurationClassPostProcessor , 所以在此處 invokeBeanFactoryPostProcessors 中,就會調用 postProcessBeanDefinitionRegistry 方法來解析所有標註有 @Configuration 註解的類,並解析該類裡面所有標註 @Bean 的方法和標註 @Import 的bean,並注入這些解析的 Bean 到 BeanFactory中。


registerBeanPostProcessors 註冊 BeanPostProcessor 到 BeanFactory 的 beanPostProcessors 列表

BeanFactoryPostProcessor 是在 Bean 實例化前對 BeanFactory 進行擴展,BeanPostProcessor 是在 Bean 實例化後對 Bean 進行擴展,BeanPostProcessor 的接口定義如下:

spring-boot 之深入理解 ApplicationContext

在 registerBeanPostProcessors 中,主要是找尋實現了 BeanPostProcessor 的Bean,將其註冊到BeanFactory 的 beanPostProcessors 屬性裡面,待後面使用。


onRefresh 為子類留下擴展,讓子類能夠繼續註冊特殊的Bean

在spring-boot 中 AnnotationConfigServletWebServerApplicationContext 的 onRefresh ,其代碼如下:

spring-boot 之深入理解 ApplicationContext

在onRefresh中創建了內嵌的web服務器。


registerListeners 註冊 ApplicationListeners

Spring 的 ApplicationContext 容 器 內 部 允 許 以 ApplicationEvent 的 形 式 發 布 事 件 , 容 器 內 注 冊 的 ApplicationListener類型的bean定義會被ApplicationContext容器自動識別,它們負責監聽容器內發佈的所有 ApplicationEvent類型的事件,一旦容器內發佈ApplicationEvent及其子類型的事件, 註冊到容器的ApplicationListener就會對這些事件進行處理。

ApplicationEvent 的一個類圖,是具體的事件類

spring-boot 之深入理解 ApplicationContext

ApplicationListener 是事件的監聽器,在spring-boot啟動的時候,會通過getSpringFactoriesInstances(ApplicationListener.class)加載所有的事件監聽器到context中。

ApplicationContext 在具體實現事件的發佈和事件監聽器的註冊功能委託給了 ApplicationEventMulticaster,整個類圖如下:

spring-boot 之深入理解 ApplicationContext

AbstractApplicationEventMulticaster 是對 ApplicationEventMulticaster 的一個抽象類實現, SimpleApplicationEventMulticaster 是 Spring 提 供 的 AbstractApplicationEventMulticaster 的一個子類實現,添加了事件發佈功能的實現。

ApplicationEventMulticaster 的初始化是在 initApplicationEventMulticaster 中完成。

在事件的發佈上,默認是同步順序發佈,但是為了避免可能存在的性能問題 , 我們可以設置TaskExecutor來實現併發發佈。

整個關係圖:

spring-boot 之深入理解 ApplicationContext


finishBeanFactoryInitialization 創建所有非lazy-init的Bean

具體是調用 beanFactory.preInstantiateSingletons 來實現的

finishRefresh 應用程序上下文刷新結束,發送事件通知

通過 ApplicationEventMulticaster 發佈 ContextRefreshedEvent事件

以上就是整個refresh的過程,refresh


總結

ApplicationContext 是Spring在 BeanFactory 基礎容器之上,提供的另一個IoC容器實現。它擁 有許多 BeanFactory 所沒有的特性,包括統一的資源加載策略、國際化信息支持、容器內事件發佈。本文重點介紹了ApplicationContext的refresh過程,指出其主要工作就是做了Bean的註冊、加載、初始化,下面一篇會繼續介紹Bean實例化的具體過程,歡迎關注。

你的鼓勵是我繼續寫下去的動力,期待我們共同進步,歡飲關注。


分享到:


相關文章: