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 基礎上增加了面向框架的特性
我們再來看一個稍微複雜點的類圖:
我們來簡要說下上面的圖,幫助理解。
第一條線,從 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的註冊:
此處配合 AnnotatedBeanDefinitionReader 完成了Bean的註冊。
ApplicationContext
ApplicationContext 是一個高級形態的 IoC 容器,ApplicationContext 在 BeanFactory 基礎上附加了好多其他功能:
MessageSource:支持不同的信息源,支持國際化實現
ResourcePatternResolver:訪問資源,使得我們可以從不同的地方獲取到Bean定義
ApplicationEventPublisher:提供事件機制,跟Bean的整個生命週期結合,方便擴展
以上是一些 ApplicationContext 提供的額外功能,正是這些功能使得 ApplicationContext 與 BeanFactory 相比,ApplicationContext 提供了更多面向框架的功能,因此我們一般使用 ApplicationContext 進行開發。
一個最簡單的 ApplicationContext 的實現是 StaticApplicationContext,主要是為了測試而用,不應該在生產環境中使用,一個簡單的使用示例:
其中重點是 refresh 方法,會涉及到IoC容器啟動的一系列複雜操作,下面我們來具體分析下。
在具體介紹 refresh 之前,我們得明確 IoC 容器的最主要功能是Bean管理,那我們就必須要經歷:
找到這些Bean定義
加載Bean定義
創建Bean
那我們就跟著上面的3步驟來看下refresh中是如何實現的。
StaticApplicationContext 自編碼實現Bean定義、註冊、創建
StaticApplicationContext 中不支持從外部配置文件中讀取Bean,只支持編程的方式來註冊Bean,我們來看下如何做:
上面的代碼中我們定義了BeanContext來註冊BeanDefinition,最後通過refresh來更新。
來看測試例子:
好了我們知道了簡單的使用規則後,我們再來看看一個稍微複雜點的 ApplicationContext:AnnotationConfigApplicationContext,這個我們就可以完整的看到Bean3 部曲:尋找,載入,創建。我們先來看尋找。
BeanDefinition 的 Resource 定位
第一個尋找其實是Resource定位過程,spring將IO訪問都抽象為Resource的訪問,在 AnnotationConfigApplicationContext 中,將 Resource 定位委託給了 ClassPathBeanDefinitionScanner ,下面我們看下主要的類圖:
其中
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, 關於資源定位,我們來看一個類圖:
AbstractApplicationContext 繼承了 DefaultResourceLoader,getResource(String)直接用DefaultResourceLoader的了
AbstractApplicationContext類的內 部聲明有一個resourcePatternResolver,類型是 ResourcePatternResolver,對應的實例類型為 PathMatchingResourcePatternResolver
PathMatchingResourcePatternResolver構造的時候會接受一個ResourceLoader,而AbstractApplicationContext本身又繼承自 DefaultResourceLoader,當然就直接把自身傳遞進去
ApplicationContext的實現類在作為ResourceLoader或者 ResourcePatternResolver時候的行為, 完全就是委派給了 PathMatchingResourcePatternResolver和DefaultResourceLoader來做。
BeanDefinition 註冊
第二步驟是Bean的註冊,也是在scan中完成:
Bean 創建
前面Bean的定位和註冊都在scan中完成了,現在終於到了refresh函數了,我們來看下refresh中做了啥,根據前面的講述,refresh中應該是開始對Bean進行創建了,實際上我們會發現 refresh 中還是會做對Bean的註冊。
上面是整個refresh過程的概括,分別說下每步做什麼。
prepareBeanFactory 標準初始化配置 BeanFactory
設置 BeanFactory 工廠創建 Bean 時使用什麼樣的類加載器,默認情況下使用線程上下文類加載器(默認為 AppClassLoader),添加 BeanPostProcessor,此處包含:ApplicationContextAwareProcessor 和 ApplicationListenerDetector。
postProcessBeanFactory 對 BeanFactory 進行個性化定製的擴展
這時候加載了 Bean 的定義,但是這時候還沒有 Bean 被實例化,允許註冊一些 BeanPostProcessors 類型的 Bean 用來在 Bean 初始化前後做一些事情。
例如 AbstractRefreshableWebApplicationContext 中就註冊了 ServletContextAwareProcessor ,用來把 servletContext 設置到實現了 ServletContextAware 接口的 Bean。
invokeBeanFactoryPostProcessors 執行 BeanFactoryPostProcessor 來對 BeanFactory 進行擴展
通過委託給 PostProcessorRegistrationDelegate 來執行所有的 BeanFactoryPostProcessor。
此處先介紹兩個接口 BeanDefinitionRegistryPostProcessor 和 BeanFactoryPostProcessor:
通過執行 BeanFactoryPostProcessor ,來對 BeanFactory 中的 Bean 定義進行修改,比如常見的是統一設置某些 Bean 的屬性變量值, 對指定的 Bean 定義進行修改或者註冊新的 Bean。
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 的接口定義如下:
在 registerBeanPostProcessors 中,主要是找尋實現了 BeanPostProcessor 的Bean,將其註冊到BeanFactory 的 beanPostProcessors 屬性裡面,待後面使用。
onRefresh 為子類留下擴展,讓子類能夠繼續註冊特殊的Bean
在spring-boot 中 AnnotationConfigServletWebServerApplicationContext 的 onRefresh ,其代碼如下:
在onRefresh中創建了內嵌的web服務器。
registerListeners 註冊 ApplicationListeners
Spring 的 ApplicationContext 容 器 內 部 允 許 以 ApplicationEvent 的 形 式 發 布 事 件 , 容 器 內 注 冊 的 ApplicationListener類型的bean定義會被ApplicationContext容器自動識別,它們負責監聽容器內發佈的所有 ApplicationEvent類型的事件,一旦容器內發佈ApplicationEvent及其子類型的事件, 註冊到容器的ApplicationListener就會對這些事件進行處理。
ApplicationEvent 的一個類圖,是具體的事件類
ApplicationListener 是事件的監聽器,在spring-boot啟動的時候,會通過getSpringFactoriesInstances(ApplicationListener.class)加載所有的事件監聽器到context中。
ApplicationContext 在具體實現事件的發佈和事件監聽器的註冊功能委託給了 ApplicationEventMulticaster,整個類圖如下:
AbstractApplicationEventMulticaster 是對 ApplicationEventMulticaster 的一個抽象類實現, SimpleApplicationEventMulticaster 是 Spring 提 供 的 AbstractApplicationEventMulticaster 的一個子類實現,添加了事件發佈功能的實現。
ApplicationEventMulticaster 的初始化是在 initApplicationEventMulticaster 中完成。
在事件的發佈上,默認是同步順序發佈,但是為了避免可能存在的性能問題 , 我們可以設置TaskExecutor來實現併發發佈。
整個關係圖:
finishBeanFactoryInitialization 創建所有非lazy-init的Bean
具體是調用 beanFactory.preInstantiateSingletons 來實現的
finishRefresh 應用程序上下文刷新結束,發送事件通知
通過 ApplicationEventMulticaster 發佈 ContextRefreshedEvent事件
以上就是整個refresh的過程,refresh
總結
ApplicationContext 是Spring在 BeanFactory 基礎容器之上,提供的另一個IoC容器實現。它擁 有許多 BeanFactory 所沒有的特性,包括統一的資源加載策略、國際化信息支持、容器內事件發佈。本文重點介紹了ApplicationContext的refresh過程,指出其主要工作就是做了Bean的註冊、加載、初始化,下面一篇會繼續介紹Bean實例化的具體過程,歡迎關注。
你的鼓勵是我繼續寫下去的動力,期待我們共同進步,歡飲關注。
閱讀更多 程序猿的進擊之路 的文章