天天看點

(Spring源碼解析)一步一步分析,springMVC項目啟動過程(二)

 這篇來看下AbstractApplicationContext中的refresh()方法,這個方法初始化且啟動整個spring容器的核心。方法名字了解起來就是重新整理的意思,意味着重新整理整個Spring容器,做好一切準備。

public void refresh() throws BeansException, IllegalStateException {
	    //初始化容器需要加鎖,防止并發加載,startupShutdownMonitor鎖對象
		synchronized (this.startupShutdownMonitor) {
			// 預加載重新整理,裡面包含初始化準備工作
			prepareRefresh();

			// 建立bean工廠,加載初始化beanDefinitionMap、依賴關系、注入屬性等等對象,注入對象解析的實作在裡面
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// 準備beanFactory綁定到context容器
			prepareBeanFactory(beanFactory);

			try {
				// 允許在context子類中對bean工廠進行後處理
				postProcessBeanFactory(beanFactory);

				// 執行個體化并調用所有已注冊的beanfactorypostprocessor bean對象
				invokeBeanFactoryPostProcessors(beanFactory);

				// 注冊攔截bean建立的bean處理器.
				registerBeanPostProcessors(beanFactory);

				// 初始化國際化消息資源
				initMessageSource();

				// 初始化應用事件廣播器,用來觸發事件監聽
				initApplicationEventMulticaster();

				// 初始化context子類的特殊bean對象
				onRefresh();

				// 注冊監聽器和事件類型,就是把所有對應的監聽器和事件類型儲存到應用容器裡
				registerListeners();

				// 執行個體化所有剩下的(非惰性初始化)單例.
				finishBeanFactoryInitialization(beanFactory);

				// 最後一步:釋出重新整理完畢相應的事件ContextRefreshedEvent
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// 出現重新整理異常,則銷毀已經建立的單例以避免占用資源。
				destroyBeans();

				// 重置激活标志
				cancelRefresh(ex);

				// 抛出異常
				throw ex;
			}

			finally {
				// 清楚部分加載解析用到的beanmeta緩存,因為我們已經在spring核心容器裡重置了常見的内部緩存 可能不再需要單身bean的中繼資料了
				resetCommonCaches();
			}
		}
	}
           

咋一看這個方法的代碼很少,其實裡面調用很多層架構所需要的實作,(初略總結)大概做了如下事情:

加載PropertySources、環境參數、初始化并建構BeanFactory、解析注冊XmlBeanDefinition,初始化建立裝載bean、解析Bean之間的依賴關系、國際化消息初始化、初始化監聽事件廣播、注冊所有監聽器、事件釋出通知結束等。

預加載方法:

protected void prepareRefresh() {
		this.startupDate = System.currentTimeMillis();//記錄啟動時間
		this.closed.set(false);//容器沒有關閉辨別
		this.active.set(true);//容器啟動激活辨別

		if (logger.isInfoEnabled()) {
			logger.info("Refreshing " + this);
		}

		// 初始化加載系統上下文環境屬性參數資源,在ContextLoader可能已加載
		initPropertySources();

		// 校驗所有的設定Required的Properties,在ConfigurablePropertyResolver#setRequiredProperties設定
		getEnvironment().validateRequiredProperties();

		// 初始化容器早期事件Set集合
		this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}
           

本章重點分析BeanFactory初始化過程:

  1. 預準備 初始化環境property sources
  2. 初始化BeanFactory,解析xml注冊配置和bean對象
  3. 建立并重新整理BeanFactory,解析注冊Bean
AbstractRefreshableApplicationContext#refreshBeanFactory方法實作
	protected final void refreshBeanFactory() throws BeansException {
		if (hasBeanFactory()) {
			destroyBeans();
			closeBeanFactory();
		}
		try {
			DefaultListableBeanFactory beanFactory = createBeanFactory();//建立BeanFactory對象
			beanFactory.setSerializationId(getId());//序列化id
			customizeBeanFactory(beanFactory);//
			loadBeanDefinitions(beanFactory);//加載資源解析BeanDefinition
			synchronized (this.beanFactoryMonitor) {
				this.beanFactory = beanFactory;
			}
		}
		catch (IOException ex) {
			throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
		}
	}
           

可以看到refreshBeanFactory中的方法loadBeanDefinitions加載資源并去解析xml,然後建立BeanDefinition等操作

XmlWebApplicationContext的具體實作loadBeanDefinitions方法,第一個參數location就是web.xml配置的configLocations路徑,第二參數actualResources的資源Set

public int loadBeanDefinitions(String location, Set<Resource> actualResources) throws BeanDefinitionStoreException {
		ResourceLoader resourceLoader = getResourceLoader();
		if (resourceLoader == null) {
			throw new BeanDefinitionStoreException(
					"Cannot import bean definitions from location [" + location + "]: no ResourceLoader available");
		}

		if (resourceLoader instanceof ResourcePatternResolver) {
			// Resource pattern matching available.
			try {
				Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
				int loadCount = loadBeanDefinitions(resources);
				if (actualResources != null) {
					for (Resource resource : resources) {
						actualResources.add(resource);
					}
				}
				if (logger.isDebugEnabled()) {
					logger.debug("Loaded " + loadCount + " bean definitions from location pattern [" + location + "]");
				}
				return loadCount;
			}
			catch (IOException ex) {
				throw new BeanDefinitionStoreException(
						"Could not resolve bean definition resource pattern [" + location + "]", ex);
			}
		}
		else {
			// Can only load single resources by absolute URL.
			Resource resource = resourceLoader.getResource(location);
			int loadCount = loadBeanDefinitions(resource);
			if (actualResources != null) {
				actualResources.add(resource);
			}
			if (logger.isDebugEnabled()) {
				logger.debug("Loaded " + loadCount + " bean definitions from location [" + location + "]");
			}
			return loadCount;
		}
	}
           

繼續閱讀