Spring原始碼學習之容器的功能擴充套件
我們都站在巨人的肩膀上
宣告:參考《Spring原始碼深度解析》
ApplicationContext和BeanFactory區別:
Application提供了更多的擴充套件功能,簡單來說,就是:Application包含了BeanFactory的所有功能。
1.Application和BeanFactory載入方式
BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
Application bf = new ClassPathXmlApplicationContext("beanFactoryTest.xml" );
2.設定配置路徑
public void setConfigLocations(String[] locations){
if(locations!=null){
...
//解析指定路徑
this.configLocations[i] = resolvePath(location[i]).trim();
...
}
}
此函式主要用於解析給定路徑陣列,如果包含特殊符號(如:${var}),則在resolvePath中會搜尋匹配的系統變數並替換。
3.在refresh函式中幾乎包含了Application中提供的全部功能:
public void refresh() throws BeansException,IlleGalStateException{
synchronized(this.startupShutdownMonitor){
//準備重新整理的上下文
prepareRefresh();
//初始化BeanFactory,進行xml讀取
ConfigurableListTableBeanFactory beanFactory = obtainFreshBeanFactory();
//對BeanFactory進行各種功能填充
prepareBeanFactory(beanFactory);
tyr{
//子類覆蓋方法做額外的處理
postProcessBeanFactory(beanFactory);
//啟用各種BeanFactory處理器
invokeBeanFactoryPostProcessors(beanFactory);
//註冊攔截Bean建立的Bean處理器,這裡只是註冊,真正的呼叫是在getBean時候
registerBeanPostProcessors(beanFactory);
//為了上下文初始化Message源,即不同語言的訊息體,國際化處理
initMessageSoure();
//初始化應用訊息廣播器,並放入“applicationEventMulticaster”bean中
initApplicationEventMulticaster();
//留給子類初始化其他的bean
onRefresh();
//在所有註冊的備案中查詢listener bean,註冊到訊息廣播中
registerListeners();
//初始化剩下的單例項(非惰性)
finishBeanFactoryInitialization(beanFactory);
//完成重新整理過程,通知生命週期處理器lifecycleProcessor重新整理過程,同時發出ContextRefreachEvent通知別人
finishRefresh();
}catch(BeanException ex){
destroyBeans();
cancelRefresh(ex);
throw ex;
}
}
}
簡單概括一下步驟:
1. 初始化前的準備工作,比如對系統屬性或者環境變數進行準備及驗證。
2. 初始化BeanFactory,並進行XML檔案讀取。
3. 對BeanFactory進行各種功能填充。(例如:@Autowired 等註解就是這一步完成)
4. 子類覆蓋方法做額外處理。這一步很少用到,書上解釋說的是,Spring優秀的開放式架構,方便程式猿在業務需求上進行進一步擴充套件已存在的功能。
5. 啟用各種BeanFactory處理器。
6. 註冊攔截bean建立的bean處理器,並不是真正的呼叫,呼叫是在getBean的時候。
7. 為上下文初始化Message源,對不同語言的訊息體進行國際化處理。
8. 初始化應用訊息廣播器,並放入“applicationEventMulticaster”bean中
9. 留給子類來初始化其他bean
10. 在所有註冊的bean中查詢listener bean,註冊到訊息廣播中。
11. 初始化剩下的單例項。
12. 完成重新整理過程,通知生命週期處理器lifecycleProcessor重新整理過程,同時發出ContextRefreachEvent通知別人
4.容器擴充套件部分介紹
這裡我們只介紹部分功能的一些處理,具體細節還請參考原書籍
1.載入BeanFactory
Spring中obtainFreshBeanFactory方法實現了BeanFactory的全部功能並且添加了大量擴充套件。經過這個函式知乎ApplicationContext就具有了BeanFactory的全部功能。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory(){
//初始化BeanFactory,並進行XMl檔案讀取,並將得到的BeanFactory記錄在當前實體的屬性中
refreshBeanFactory();
//返回當前實體的beanFactory屬性
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
...
return beanFactory ;
}
//具體實現方法交給這個方法
protected final void refreshBeanFactory() throws BeansException{
...
try{
DefaultListableBeanFactory beanFactory = createBeanFactory();
//為了序列化指定id,如果需要的話,讓這個BeanFactory從id反序列化到BeanFactory物件
beanFactory.setSerializationId(getId());
//定製beanFactory,設定相關屬性,包括是否允許覆蓋同名稱的不同定義的物件以及迴圈依賴以及設定@Autowried和@Qualifier註解直譯器QualifierAnnotationAutowrieCandidateResolver
customizeBeanFactory(beanFactory);
//初始化DodumentReader,並進行XML檔案讀取及解析
LoadBeanDefinitions(beanFactory);
synchronized(this.beanFactoryMonitor){
this.beanFactory = beanFactory;
}
}catch(IOException ex){
...
}
}
步驟解析:
1. 建立DefaultListableBeanFactory 。DefaultListableBeanFactory 是容器的基礎,必須要首先例項化。
2. 指定序列化ID。
3. 定製BeanFactory。
4. 載入BeanDefinition。
5. 使全域性變數記錄BeanFactory類例項。因為DefaultListableBeanFactory 型別的遍歷beanFactory是函式內的區域性變數,所以要使用全域性變數記錄分析結果。
2.定製BeanFactory
在基本容器的基礎上,增加了是否允許覆蓋是否允許誇張的設定並提供了註解@Qualifier和@Autowired的支援。
在原始碼中是否允許覆蓋和依賴只是判斷了是否為空,如果不為空要進行設定,那麼就使用子類覆蓋方法。
public class ... entends ClassPathXmlApplicationContext{
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory){
//是否允許覆蓋通名稱的不同定義物件
super.setAllowBeanDefinitionOverriding(false);
//是否允許bean之間存在迴圈依賴
super.setAllowCirularRefernces(false);
super.customizeBeanFactory(beanFactory);
}
}
3.註冊監聽器
protected void registerListeners(){
//硬編碼方式註冊的監聽器
for(ApplicationListener<?> listener:getApplicationListeners()){
getApplicationEventMulticaster().addApplicationListener(listener);
}
//配置檔案註冊的監聽器處理
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class,true,false);
for(String lisName : listenerBeanNames){
getApplicationEventmulticaster().addApplicationlistenerBean(lisName);
}
}
4.finishRefresh
在Spring中還提供Lifecycle介面,Lifecycle中包含start/stop方法,實現此介面後Spring會保證在啟動的時候呼叫start方法開始生命週期,並在Spring關閉的時候呼叫stop方法結束生命週期,通常用來配置後臺程式猿,在啟動後一直執行(入隊MQ輪詢等)。而ApplicationContext的初始化最後證實保證了這一功能的實現。
protected void finishRefresh(){
initlifecycleProcessor();
getLifecycleProcessor().onRefresh();
publishEvent(new ContextRefreshedEvent(this));
}
- initLifecycleProcessor。當ApplicationContext啟動或者停止的時候,會通過LifecycleProcessor來生命與所有生命的bean的週期做狀態更新,而在LifecycleProcessor試用期首先需要初始化。
- onRefresh。啟動所有實現了Lifecycle介面的bean。
- publicEvent。當完成ApplicationContext初始化的時候,要通過Spring中的時間釋出機制發出ContextRefreshedEvent事件,以保證對應的監聽器可以做到進一步的邏輯處理。