
上一節的創建了容器物件,核心就是創建了Context和BeanFactory物件,內部初始化了Reader和Scanner,加載了一些內部Bean等,
已經分析的邏輯代碼如下:
public ConfigurableApplicationContext run(String... args) {
//DONE 擴展點 SpringApplicationRunListeners listeners.starting();
//DONE 組態檔的處理和抽象封裝 ConfigurableEnvironment
//容器相關處理
//1)核心就是創建了Context和BeanFactory物件,內部初始化了Reader和Scanner,加載了一些內部Bean
context = createApplicationContext();
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] {ConfigurableApplicationContext.class }, context);
//2) TODO 容器物件中還需要準備哪些東西?
prepareContext(context, environment, listeners, applicationArguments,printedBanner);
//3) TODO
refreshContext(context);
//其他邏輯
}
這一節我們來看看,創建容器后,容器物件中還需要準備或者說設定哪些東西,并且還執行了容器哪些擴展點呢,一起來看下吧!
prepareContext()的核心脈絡
prepareContext()說白了點其實就是給容器Context和容器DefaultListableBeanFactory設定一些屬性, 你帶著這個思路去理解,就會抓大放小,關注核心即可,大致如下圖:

那么接下來,就來看下代碼到底做了些什么?
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
listeners.contextLoaded(context);
}
先給大家概括一張圖,主要可以劃分為三部分非常重要的操作、值得一提的操作、不重要的操作,如下圖:

接下來分別帶大家一起仔細分析下,
觸發的擴展點(非常重要的操作)
1)applyInitializers()觸發的擴展點操作
這個擴展操作,其實就是執行了List的一系列擴展操作,你還記得么?
這個List是在new SpringApplication的時候掃描到的類,還記得下圖么:

那么重點是這些擴展操作做了什么呢?
其實概括成一句話就是:Initializers之后,為context初始化了2個BeanFactoryPostProcessor ,補充了3個ApplicationLisenter
具體每一個ApplicationContextInitializer的執行你可以通過斷點自己仔細看下知道了,其實沒有什么復雜的邏輯,
整體如下圖所示:

術語普及BeanFactoryPostProcessor是什么?
BeanFactoryPostProcessor是什么?
之前我們遇見過擴展點有SpringApplicationRunListeners和ApplicationContextInitializer
SpringApplicationRunListeners通過一個EventPublishingRunListener,對一個List<ApplicationLisenter>做不同的事件廣播做對流程、容器和配置進行 擴展,
ApplicationContextInitializer 通過List<ApplicationContextInitializer> 其實也是整個流程中的一個擴展,
而這里BeanFactoryPostProcessor其實對容器特有的擴展,或者說是增強處理, 在容器的使用程序中,執行List<BeanFactoryPostProcessor> 對應的方法,進行擴展點的執行,很多第三方框架就是從這地方入手進行擴展的,之后會看到的,
2) 觸發listener對容器擴展操作,
除了上面ApplicationContextInitializer的擴展執行,另一個擴展操作的執行就是SpringApplicationRunListeners的擴展了,
主要有兩次觸發,listeners#contextPrepared()和listeners.contextLoaded(context);
之前我們分析過,SpringApplicationRunListeners通過一個EventPublishingRunListener,對一個List做不同的事件廣播做對流程、容器和配置進行擴展,這里廣播的是contextPreparedEvent,contextLoadedEvent,
具體細節也不在這里展開了,簡單的,這兩個事件分別可以概括為如下兩句話:
contextPreparedEvent的這里執行了2個ApplicationListener的實作,只不過這兩個listener的onApplicationEvent幾乎是什么都沒做,只是注冊兩個日志物件到容器DefaultListableBeanFactory的singletonObjects屬性,
contextLoadedEvent執行了,4個ApplicationListener,其中1個Listener往容器Context中增加了BeafactoryPostProcessor其余四個Listener基本上什么都沒干,

可以看出,這幾個擴展點核心其實也沒有做很復雜的事情,就是給容器物件補充設定了一些屬性而已,可以概括如下圖所示:

beanFactory的一些屬性補充(值得一提的操作)
除了上述比較重要的操作外,prepareContext中還有一些比較值得一提的操作,讓我們
1)beanFactory.registerSingleton
注冊兩個物件到容器springApplicationArguments、springBootBanner到beanFactory的 singletonObjects 屬性
2)補充BeanDefinition
BeanDefinitionLoader.load() 補充了 LearnSpringBootApplication 的BeanDefinition到beanFactory中,
這些都比較簡單,整體如下圖:

其實這里關鍵的是容器內的兩個屬性的設定:
一個是【核心屬性】Map<String,Object> singletonObjects 容器存放Bean物件的集合
一個是【核心屬性】Map<String, BeanDefinition> beanDefinitionMap 容器存放beanDefinition物件的集合
這個是我們這里想要強調的一點,
設定幾個屬性或者組件(不重要的操作)
1)context#setEnvironment () 設定envrioment 到context中,也就是讓容器持有組態檔的封裝物件而已,
2)resourceLoader 設定resource類加載器 到context容器 ,默認沒有resourceLoader ,所以這里什么沒干,
3)addConversionService 添加轉換器和格式化器 到context容器,不知道這個組件時做啥的,暫時不是很重要,
4)logStartupInfo() 輸出啟動日志-PID,啟動檔案目錄
5)logStartupProfileInfo() 輸出啟動日志-使用的profile
6)設定容器屬性lazyInitialization、allowBeanDefinitionOverriding,默認都是false,不懶加載和不覆寫BeanDefinition,

小結
說白了,prepareContext()就是給容器Context、BeanFactory設定了一堆屬性和組件,執行了initialize/listener的擴展點,
主要給容器如下幾個核心屬性設定值:
singletonObjects 、beanDefinitionMap 、beanFactoryPostProcessors、applicationListeners,
prepareContext()準備完成之后,接下來就是容器關鍵的擴展操作執行了,也是很多容器功能和第三方功能的擴展之處,我們下一節來一起看下吧!
本文由博客群發一文多發等運營工具平臺 OpenWrite 發布
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/304808.html
標籤:其他
