Spring 原始碼 (2)Spring IOC 容器 前戲準備工作

玲丶蹊發表於2022-04-15

Spring 最重要的方法refresh方法

根據上一篇文章 https://www.cnblogs.com/redwinter/p/16141285.html Spring Bean IOC 的建立流程繼續解讀Spring原始碼,本篇文章解讀Spring 原始碼最重要的方法refresh方法。

這個方法位於:AbstractApplicationContext#refresh,這個方法中總共有15個方法,Spring原始碼的精髓就是這15個方法中。

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			// 準備工作,載入環境變數等操作
			// 1、設定容器啟動時間
			// 2、設定停止狀態為false
			// 3、設定活躍狀態為true
			// 4、獲取Environment物件,並設定屬性值
			// 5、設定監聽器和事件的集合,模式為空的集合
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			// 告訴子類重新整理內部 bean 工廠, 獲取重新整理bean的工廠: DefaultListableBeanFactory
			// 並且載入BeanDefinition
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			// 準備BeanFactory 設定一些屬性
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
                 // 允許子類進行擴充套件BeanFactoryPostProcessor
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				// 例項化並執行BeanFactoryPostProcessor
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				// 例項化並註冊BeanPostProcessor
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				// 國際化設定
				initMessageSource();

				// Initialize event multicaster for this context.
				// 例項化事件多播器
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.
				// 初始化特定上下文子類中的其他特殊bean,web容器
				onRefresh();

				// Check for listener beans and register them.
				// 檢查listener bean 並註冊它們
				// 註冊監聽器
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				// 例項化所有剩餘的(非惰性初始化)單例。
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				// 釋出相應的事件
				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.
				// 銷燬Bean
				destroyBeans();

				// Reset 'active' flag.
				// 重置 active 標誌
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

前戲準備 prepareRefresh 方法

Spring的前戲準備大概就是做了以下幾件事:

  • 設定容器的啟動時間
  • 設定容器的停止狀態為false
  • 設定容器的啟用狀態為true
  • 獲取環境資訊並驗證必要的屬性
  • 準備監聽器和事件的容器
protected void prepareRefresh() {
		// Switch to active.
		// 設定啟動時間 設定標識位
		this.startupDate = System.currentTimeMillis();
		// 設定容器停止標識為false
		this.closed.set(false);
		// 設定容器啟用標識為true
		this.active.set(true);
		// Initialize any placeholder property sources in the context environment.
		// 初始化上下文環境中的任何佔位符屬性源
		// 留給子類進行擴充套件,比如新增必須的屬性值驗證
		initPropertySources();

		// Validate that all properties marked as required are resolvable:
		// see ConfigurablePropertyResolver#setRequiredProperties
		// 獲取環境物件,並驗證需要的屬性
		getEnvironment().validateRequiredProperties();

		// Store pre-refresh 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<>();
	}

這裡有個問題就是他的環境資訊是何時設定進去的呢?

實際上是在容器啟動時呼叫了父類建構函式時設定進去的,Environment他是一個介面,他有個重要的實現類叫StandardEnvironment ,在Spring啟動的時候就會使用這個類進行環境資訊的載入,最終他會呼叫到System#getPropertiesSystem#getenv方法,然後將載入到屬性放在Map中進行儲存。

大概的流程如下:

標記的類就是Environment環境資訊的載入過程呼叫的類,最終會呼叫到System#getPropertiesSystem#getenv方法,然後完成環境資訊的載入,主要載入的資訊就是系統的環境變數,比如在Windows中配置的環境變數或者啟動類中使用-D引數配置的啟動引數都會進行載入到StandardEnvironment 這個類中,類似於使用-Dxxx.name=123這種引數會載入到systemProperties中,配置的windows環境變數會載入systemEnvironment中。

這個就是Spring IOC 建立的第一個方法的前戲準備工作,接下來解讀預設的BeanFactory實現類DefaultListableBeanFactory的建立過程。

相關文章