上一節的建立了容器物件,核心就是建立了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
先給大家概括一張圖,主要可以劃分為三部分
非常重要的操作、值得一提的操作、不重要的操作
,如下圖:
接下來分別帶大家一起仔細分析下。
觸發的擴充套件點(非常重要的操作)
1)applyInitializers()觸發的擴充套件點操作
這個擴充套件操作,其實就是執行了List的一系列擴充套件操作。你還記得麼?
這個List是在new SpringApplication的時候掃描到的類。還記得下圖麼:
那麼重點是這些擴充套件操作做了什麼呢?
其實概括成一句話就是:
Initializers之後,為context初始化了2個BeanFactoryPostProcessor ,補充了3個ApplicationLisenter
具體每一個ApplicationContextInitializer的執行你可以透過斷點自己仔細看下知道了,其實沒有什麼複雜的邏輯。
整體如下圖所示:
術語普及BeanFactoryPostProcessor是什麼?
BeanFactoryPostProcessor是什麼?之前我們遇見過擴充套件點有SpringApplicationRunListeners和ApplicationContextInitializerSpringApplicationRunListeners透過一個EventPublishingRunListener,對一個List
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 釋出!