spring原始碼之refresh第二篇

程式設計師田同學發表於2022-01-10

大家好,我是程式設計師田同學

上篇文章對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 並沒有完成初始化)

image-20220110103559050

點進去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看下這個繼承圖大概就明瞭。

image-20220110105719840

  // 設定 BeanFactory 的兩個配置屬性:是否允許 Bean 覆蓋、是否允許迴圈引用
  customizeBeanFactory(beanFactory);

image-20220110110304120

這個方式只是一個設定,設定是否允許迴圈依賴,至於什麼是迴圈依賴呢?也就是 A-B-C之間他們相互依賴,spring有一套自己的機制去處理迴圈依賴,以後文章為進行分析,這一步僅僅是配置是否允許迴圈依賴,讀者清楚就可以了。

   // 這個方法將根據配置,載入各個 Bean,然後放到 BeanFactory 中
  loadBeanDefinitions(beanFactory);

經過上面一系列的步驟,一個beandefintion就形成,beandefintion就是我們常說的bean,也就是一個物件的加強版。

接下來就需要把這個bean加入到beanfactory中了,這一步交給loadBeanDefinitions()方法去執行。

image-20220110111314794

spring方法命名確實精妙,只看看方法名大概也知道每個方法幹了什麼!

建立一個beanDefinitionReader(bean閱讀器)去讀取xml中的bean,雖然xml很少用了,但是用它來舉例還是很經典的。

真正幹活的是loadBeanDefinitions(beanDefinitionReader),往下走很漫長漫長,把我們xml中的bean解析成BeanDefinition,並呼叫registerBeanDefinition()方法把它註冊到註冊中心,傳送註冊事件。

總結一下,到這裡已經初始化了 Bean 容器,<bean /> 配置也相應的轉換為了一個個 BeanDefinition,然後註冊了各個 BeanDefinition 到註冊中心,並且傳送了註冊事件。

到此obtainFreshBeanFactory() 方法也就正式結束了。

spring的呼叫過程鏈路非常非常的長,一步步點進去沒一會你就迷了,田同學認為比較好的一個辦法就是,先站在方法體外看這個方法幹了什麼,然後逐步拆分進入到每一個方法中。

站在refresh()外看這兩個方法,prepareRefresh()準備一下重新整理要做的事,obtainFreshBeanFactory()註冊好bean並加入到beanfactory中。

好啦,今天的spring原始碼分析就到這裡了。

相關文章