大家好,我是程式設計師田同學
上篇文章對spring核心啟動方法refresh做了整體的解讀,但是隻是泛泛而談,接下來會出一系統文章對每個方法的原始碼進行深刻解讀。
第一篇文章見 spring原始碼之方法概覽
首先,第一個方法是prepareRefresh()方法,這個方法做的事很簡單,也不是本文的重點。該方法記錄容器的啟動時間,初始化監聽容器。
protected void prepareRefresh() {
// Switch to active
//紀錄啟動時間
this.startupDate = System.currentTimeMillis();
System.out.println("spring啟動時間為--------------------" + this.startupDate);
this.closed.set(false);
System.out.println("spring標記為未關閉--------------------" + this.closed);
this.active.set(true);
System.out.println("spring當前啟用狀態--------------------" + this.active);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
} else {
logger.debug("Refreshing " + getDisplayName());
}
}
// Initialize any placeholder property sources in the context environment.
//空方法
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
//校驗 xml配置檔案
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
//初始化applicationListeners監聽容器
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<> (this.applicationListeners);
} else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
this.earlyApplicationEvents = new LinkedHashSet<>();
}
讀者大致摟一眼即可,對這個方法整體就能很快把握。
接下來才是今天的重頭戲——obtainFreshBeanFactory()方法,是refresh()方法中的第二個方法,也是整個refresh()方法中核心方法之一。
該方法主要的作用是,這裡將會初始化 BeanFactory、載入 Bean、註冊 Bean 等等。(Bean 並沒有完成初始化)
點進去obtainFreshBeanFactory()方法我們一探究竟。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 關閉舊的 BeanFactory (如果有),建立新的 BeanFactory,載入 Bean 定義、註冊 Bean 等等
refreshBeanFactory();
// 返回剛剛建立的 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
refreshBeanFactory()應該是這個方法的重頭戲,我們再深入進去。
@Override
protected final void refreshBeanFactory() throws BeansException {
// 如果 ApplicationContext 中已經載入過 BeanFactory 了,銷燬所有 Bean,關閉 BeanFactory
// 注意,應用中 BeanFactory 本來就是可以多個的,這裡可不是說應用全域性是否有 BeanFactory,而是當前ApplicationContext 是否有 BeanFactory
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
// 初始化一個 DefaultListableBeanFactory,為什麼使用這個BeanFactory?因為這是最牛的 BeanFactory。
DefaultListableBeanFactory beanFactory = createBeanFactory();
// 用於 BeanFactory 的序列化
beanFactory.setSerializationId(getId());
// 設定 BeanFactory 的兩個配置屬性:是否允許 Bean 覆蓋、是否允許迴圈引用
customizeBeanFactory(beanFactory);
// 載入 Bean 到 BeanFactory 中
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
簡單提一句,DefaultListableBeanFactory為什麼是最牛的BeanFactory看下這個繼承圖大概就明瞭。
// 設定 BeanFactory 的兩個配置屬性:是否允許 Bean 覆蓋、是否允許迴圈引用
customizeBeanFactory(beanFactory);
這個方式只是一個設定,設定是否允許迴圈依賴,至於什麼是迴圈依賴呢?也就是 A-B-C之間他們相互依賴,spring有一套自己的機制去處理迴圈依賴,以後文章為進行分析,這一步僅僅是配置是否允許迴圈依賴,讀者清楚就可以了。
// 這個方法將根據配置,載入各個 Bean,然後放到 BeanFactory 中
loadBeanDefinitions(beanFactory);
經過上面一系列的步驟,一個beandefintion就形成,beandefintion就是我們常說的bean,也就是一個物件的加強版。
接下來就需要把這個bean加入到beanfactory中了,這一步交給loadBeanDefinitions()方法去執行。
spring方法命名確實精妙,只看看方法名大概也知道每個方法幹了什麼!
建立一個beanDefinitionReader(bean閱讀器)去讀取xml中的bean,雖然xml很少用了,但是用它來舉例還是很經典的。
真正幹活的是loadBeanDefinitions(beanDefinitionReader),往下走很漫長漫長,把我們xml中的bean解析成BeanDefinition,並呼叫registerBeanDefinition()方法把它註冊到註冊中心,傳送註冊事件。
總結一下,到這裡已經初始化了 Bean 容器,<bean />
配置也相應的轉換為了一個個 BeanDefinition,然後註冊了各個 BeanDefinition 到註冊中心,並且傳送了註冊事件。
到此obtainFreshBeanFactory() 方法也就正式結束了。
spring的呼叫過程鏈路非常非常的長,一步步點進去沒一會你就迷了,田同學認為比較好的一個辦法就是,先站在方法體外看這個方法幹了什麼,然後逐步拆分進入到每一個方法中。
站在refresh()外看這兩個方法,prepareRefresh()準備一下重新整理要做的事,obtainFreshBeanFactory()註冊好bean並加入到beanfactory中。
好啦,今天的spring原始碼分析就到這裡了。