本系列大量參考Spring IOC 容器源碼分析哗伯、 【死磕 Spring】—– IOC 總結(jié)、程序員囧輝的CSDN
上一篇說到篷角,向beanFactory
設(shè)置了各個beanFactory
所需的功能焊刹,執(zhí)行BeanFactoryPostProcessors
和注冊BeanPostProcessors
后。這次簡單提到一些國際化組件恳蹲、事件廣播器和事件監(jiān)聽器(自認(rèn)為這寫的并不好虐块,可以略過)。這篇算是水文
文章內(nèi)容如下:
1.AbstractApplicationContext#refresh()
//初始化國際化組件
2.AbstractApplicationContext#initMessageSource
//初始化事件廣播器
3.AbstractApplicationContext#initApplicationEventMulticaster
//在初始化非懶加載的bean之前初始化一下特殊的bean,交由子類實(shí)現(xiàn)
4. AbstractApplicationContext#onRefresh()
//注冊事件監(jiān)聽器
5. AbstractApplicationContext#registerListeners()
//實(shí)例化所有剩余的非懶加載單例 bean(簡單介紹)
6. AbstractApplicationContext# finishBeanFactoryInitialization(beanFactory)
1.refresh
public void refresh() throws BeansException, IllegalStateException {
// 來個鎖嘉蕾,不然 refresh() 還沒結(jié)束贺奠,你又來個啟動或銷毀容器的操作,那不就亂套了嘛
synchronized (this.startupShutdownMonitor) {
// 準(zhǔn)備工作错忱,記錄下容器的啟動時(shí)間儡率、標(biāo)記“已啟動”狀態(tài)、處理配置文件中的占位符
prepareRefresh();
// 這步比較關(guān)鍵以清,這步完成后儿普,配置文件就會解析成一個個 Bean 定義,注冊到 BeanFactory 中掷倔,
// 當(dāng)然眉孩,這里說的 Bean 還沒有初始化,只是配置信息都提取出來了勒葱,
// 注冊也只是將這些信息都保存到了注冊中心(說到底核心是一個 beanName-> beanDefinition 的 map)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 設(shè)置 BeanFactory 的類加載器浪汪,添加幾個 BeanPostProcessor,手動注冊幾個特殊的 bean
// 這塊待會會展開說
prepareBeanFactory(beanFactory);
try {
// 【這里需要知道 BeanFactoryPostProcessor 這個知識點(diǎn)凛虽,Bean 如果實(shí)現(xiàn)了此接口死遭,
// 那么在容器初始化以后,Spring 會負(fù)責(zé)調(diào)用里面的 postProcessBeanFactory 方法凯旋⊙教叮】
// 這里是提供給子類的擴(kuò)展點(diǎn),到這里的時(shí)候瓦阐,所有的 Bean 都加載蜗侈、注冊完成了篷牌,但是都還沒有初始化
// 具體的子類可以在這步的時(shí)候添加一些特殊的 BeanFactoryPostProcessor 的實(shí)現(xiàn)類或做點(diǎn)什么事
postProcessBeanFactory(beanFactory);
// 調(diào)用 BeanFactoryPostProcessor 各個實(shí)現(xiàn)類的 postProcessBeanFactory(factory) 方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注冊 BeanPostProcessor 的實(shí)現(xiàn)類睡蟋,注意看和 BeanFactoryPostProcessor 的區(qū)別
// 此接口兩個方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
// 兩個方法分別在 Bean 初始化之前和初始化之后得到執(zhí)行。注意枷颊,到這里 Bean 還沒初始化
registerBeanPostProcessors(beanFactory);
// 初始化當(dāng)前 ApplicationContext 的 MessageSource戳杀,國際化這里就不展開說了该面,不然沒完沒了了
initMessageSource();
// 初始化當(dāng)前 ApplicationContext 的事件廣播器,這里也不展開了
initApplicationEventMulticaster();
// 從方法名就可以知道信卡,典型的模板方法(鉤子方法)隔缀,
// 具體的子類可以在這里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
onRefresh();
// 注冊事件監(jiān)聽器,監(jiān)聽器需要實(shí)現(xiàn) ApplicationListener 接口傍菇。這也不是我們的重點(diǎn)猾瘸,過
registerListeners();
// 重點(diǎn),重點(diǎn)丢习,重點(diǎn)
// 初始化所有的 singleton beans
//(lazy-init 的除外)
finishBeanFactoryInitialization(beanFactory);
// 最后牵触,廣播事件,ApplicationContext 初始化完成
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
// 銷毀已經(jīng)初始化的 singleton 的 Beans咐低,以免有些 bean 會一直占用資源
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// 把異常往外拋
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
2.AbstractApplicationContext#initMessageSource 初始化國際化組件
先來看看initMessageSource
方法
protected void initMessageSource() {
//獲取Bean工廠揽思,一般是DefaultListBeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//首先判斷beanFactory的BeanDifinition緩存里有沒有id為messageSource的BeanDifinition
if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
//如果有,則從Bean工廠得到這個bean對象
this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
//當(dāng)父類Bean工廠不為空见擦,并且這個bean對象是HierarchicalMessageSource類型
if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
//強(qiáng)轉(zhuǎn)為HierarchicalMessageSource
HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
if (hms.getParentMessageSource() == null) {
//如果父消息源為空钉汗,那么設(shè)置一個父消息源
// (如果父類是AbstractApplicationContext,則返回父類的messageSource鲤屡,否則直接返回一個父類)
hms.setParentMessageSource(getInternalParentMessageSource());
}
}
if (logger.isTraceEnabled()) {
logger.trace("Using MessageSource [" + this.messageSource + "]");
}
}
else {
//如果沒有這個id為messageSource的BeanDifinition损痰,新建DelegatingMessageSource類作為messageSource的Bean
//因?yàn)镈elegatingMessageSource類實(shí)現(xiàn)了HierarchicalMessageSource接口,而這個接口繼承了MessageSource這個類
//因此實(shí)現(xiàn)了這個接口的類酒来,都是MessageSource的子類徐钠,因此DelegatingMessageSource也是一個MessageSource
DelegatingMessageSource dms = new DelegatingMessageSource();
//給這個DelegatingMessageSource添加父類消息源
dms.setParentMessageSource(getInternalParentMessageSource());
this.messageSource = dms;
//將這個messageSource實(shí)例注冊到Bean工廠中
beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
if (logger.isTraceEnabled()) {
logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
}
}
}
簡單解釋下國際化的意義;
假設(shè)我們正在開發(fā)一個支持多國語言的Web應(yīng)用程序,要求系統(tǒng)能夠根據(jù)客戶端的系統(tǒng)的語言類型返回對應(yīng)的界面:英文的操作系統(tǒng)返回英文界面役首,而中文的操作系統(tǒng)則返回中文界面——這便是典型的i18n國際化問題尝丐。
對于有國際化要求的應(yīng)用系統(tǒng),我們不能簡單地采用硬編碼的方式編寫用戶界面信息衡奥、報(bào)錯信息等內(nèi)容爹袁,而必須為這些需要國際化的信息進(jìn)行特殊處理。簡單來說矮固,就是為每種語言提供一套相應(yīng)的資源文件失息,并以規(guī)范化命名的方式保存在特定的目錄中,由系統(tǒng)自動根據(jù)客戶端語言選擇適合的資源文件档址。
3.AbstractApplicationContext#initApplicationEventMulticaster 初始化事件廣播器
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
//首先判斷beanFactory的BeanDifinition緩存里有沒有id為applicationEventMulticaster的BeanDifinition
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
//如果有盹兢,就實(shí)例化這個BeanDifinition并賦值給本地的變量applicationEventMulticaster
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
} else {
//如果沒有,那么就直接創(chuàng)建一個SimpleApplicationEventMulticaster賦值給本地變量
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
// 并且把這個SimpleApplicationEventMulticaster對象注冊到beanFactory的緩存中守伸,id為applicationEventMulticaster
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
我們就可以在其他組件要派發(fā)事件绎秒,自動注入這個
applicationEventMulticaster
,他可以管理很多個ApplicationListener
對象尼摹。并將事件發(fā)布給這些監(jiān)聽器见芹。
4. AbstractApplicationContext#onRefresh() 在初始化非懶加載的bean之前初始化一下特殊的bean
onRefresh()
則是一個空方法剂娄,交由子類自己實(shí)現(xiàn),在初始化非懶加載的bean之前初始化一下特殊的bean
玄呛。(看自己的業(yè)務(wù)要怎么做啦)
5. AbstractApplicationContext#registerListeners() 注冊事件監(jiān)聽器
protected void registerListeners() {
// 獲取硬編碼進(jìn)來的ApplicationListeners(指非配置的阅懦,通過代碼直接添加進(jìn)來的)
//注冊 到我們上面剛初始化的ApplicationEventMulticaster 中。
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
//從容器中獲取所有實(shí)現(xiàn)了ApplicationListener接口的bd的bdName,注冊到ApplicationEventMulticaster 中
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// 發(fā)布早期 earlyApplicationEvents 事件徘铝,到 ApplicationListener 們,默認(rèn)為空
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (earlyEventsToProcess != null) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
有關(guān)Spring的事件機(jī)制簡單了解可以參考下面這兩個鏈接耳胎,有機(jī)會再細(xì)說。
spring事件機(jī)制
Spring事件驅(qū)動過程分析
6. AbstractApplicationContext# finishBeanFactoryInitialization(beanFactory) 實(shí)例化所有剩余的非懶加載單例 bean(簡單介紹)
上面都是簡單介紹惕它,接下來就是主菜了
該方法會實(shí)例化所有剩余的非懶加載單例 bean场晶。除了一些內(nèi)部的 bean、實(shí)現(xiàn)了 BeanFactoryPostProcessor
接口的 bean怠缸、實(shí)現(xiàn)了BeanPostProcessor
接口的 bean(上述的這三類在前面方法里已經(jīng)被實(shí)現(xiàn)了)诗轻,其他的非懶加載單例bean
都會在這個方法中被實(shí)例化,并且 BeanPostProcessor
的觸發(fā)也是在這個方法中揭北。
這邊我們簡單介紹下扳炬,這篇就簡單水一下吧,因?yàn)檫@個方法比較重要搔体,調(diào)用棧又特別深恨樟,需要開好幾個章節(jié)來講。