Spring源碼系列 — 構造和初始化上下文

探索spring源碼實現,精華的設計模式,各類jdk提供的陌生api,還有那麼點黑科技都是一直以來想作的一件事!可是讀源碼是一件很是痛苦的事情,須要有很大的耐心和紮實的基礎。spring

在曾經讀兩次失敗的基礎上,此次但願能一站到底!這個系列基於spring v4.3.20版本探索。編程

###Spring上下文啓動加載過程的分段設計模式

spring上下文的實現很是多,其中基於Xml啓動的有ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等等。這些上下文都很是相似,基於解析Xml,獲取配置的bean,最終完成上下文的啓動加載過程。api

這裏以ClassPathXmlApplicationContext爲主分析Spring ApplicatonContext整個啓動過程。這裏將spring上下文的啓動分爲兩個大步驟:函數

  • 上下文對象自己的構造和初始化:建立ClassPathXmlApplicationContext對象,構造器中執行一些初始化操做,如:設置上下文的環境Enviroment、設置上下文的資源解析器等
  • 上下文獲取bean配置,解析實例化bean:構造BeanFactory,解析Xml,構造bean定義,依賴注入,實例化bean。這個過程很是複雜;

<div align="center"><image src="https://img2018.cnblogs.com/blog/1286175/201811/1286175-20181105095558497-955412634.jpg" width="300" high="20"></image> </div>this

這節主要分析第一個階段:上下文對象自己的構造和初始化。spa

###上下文對象自己的構造和初始化 編寫debug代碼,構造ClassPathXmlApplicationContext對象:debug

ApplicationContext context = new ClassPathXmlApplicationContext("/beans.xml");
HelloWorldBean h = context.getBean("helloWorld", HelloWorldBean.class);
h.printHelloWorld();

編寫配置文件beans.xml,配置helloWorld的bean:設計

<bean id="helloWorld" class="com.learn.ioc.beans.HelloWorldBean"></bean>

下面主要分析ClassPathXmlApplicationContext new的過程。ClassPathXmlApplicationContext構造函數以下:code

// 使用beans.xml做爲上下文的配置文件構造ApplicationContext對象
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
	this(new String[] {configLocation}, true, null);
}

// 以配置文件、是否刷新上下文、父上下文做爲參數構造ApplicationContext對象
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
		throws BeansException {
	super(parent);
	setConfigLocations(configLocations);
	if (refresh) {
		refresh();
	}
}

super(parent)和setConfigLocations(configLocations)方法對應兩階段中的第一階段:上下文對象自己的構造和初始化。refresh()方法完成第二階段:上下文獲取bean配置,解析實例化bean

super(parent)以該上下文的父上下文做爲參數調用父類的構造器,這裏因爲沒有父上下文,因此爲null

public AbstractXmlApplicationContext(ApplicationContext parent) {
	super(parent);
}

AbstractXmlApplicationContext是以Xml做爲配置的Spring上下文,AbstractXmlApplicationContext又繼續調用其父類的構造器:

public AbstractRefreshableConfigApplicationContext(ApplicationContext parent) {
	super(parent);
}

AbstractRefreshableConfigApplicationContext也是以Xml文件做爲基礎配置的上下文,可是它具備能夠刷新配置文件的能力,AbstractRefreshableConfigApplicationContext中提供了setConfigLocations方法能夠用於設置配置文件。是Xml配置文件上下文的基石。 該上下文中又調用父類構造函數:

public AbstractRefreshableApplicationContext(ApplicationContext parent) {
	super(parent);
}

Spring上下文能夠稱做爲上下文家族,繼承有多代。其中AbstractRefreshableApplicationContext是上下文中家族中的元老。它提供了兩個很是重要的接口refreshBeanFactory()和loadBeanDefinitions(DefaultListableBeanFactory beanFactory)用於配置BeanFactory和定義接下載入Bean的接口。雖然構造函數中依然調用父類的構造函數,可是它的確很是重要:

public AbstractApplicationContext(ApplicationContext parent) {
	this();
	// 設置該上下文的父上下文
	setParent(parent);
}

public AbstractApplicationContext() {
	// 初始化資源解析器,用於解析獲取Xml配置
	this.resourcePatternResolver = getResourcePatternResolver();
}

上下文家族的鼻祖AbstractApplicationContext中定義了整個Bean的聲明週期和處理過程。在構造器中初始化resourcePatternResolver資源解析器,賦予ApplicationContext具備解析資源的能力。且設置父上下文。

getResourcePatternResolver主要用來建立資源解析器:

protected ResourcePatternResolver getResourcePatternResolver() {
	return new PathMatchingResourcePatternResolver(this);
}

PathMatchingResourcePatternResolver主要以路徑匹配的模式進行解析獲取資源,其主要能力和實現後續會詳細介紹。

setParent(parent)主要用於設置該上下文的父上下文:

public void setParent(ApplicationContext parent) {
	// 設置父上下文
	this.parent = parent;
	// 若是父上下文不空
	if (parent != null) {
	  	// 獲取父上下文的環境變量Environment
		Environment parentEnvironment = parent.getEnvironment();
		if (parentEnvironment instanceof ConfigurableEnvironment) {
			// 將父上下文的環境變量Environment合併至該級上下文環境變量中
			getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
		}
	}
}

通過一系列的父類的構造器的調用,Spring上下文完成了多級上下文的載入過程。能夠從下圖看出其繼承順序關係:

<div align="center"><image src="https://img2018.cnblogs.com/blog/1286175/201811/1286175-20181105095630158-1281780770.jpg" width="450" high="20"></image> </div>

Tips
上下文的實現中設計模式很是強,秉着設計的六大原則進行實現:
單一職責原則:每種上下文都有本身的職責能力,使得上下文的擴展能力極強;
開閉原則:AbstractRefreshableApplicationContext提供了對loadBeanDefinitions的定義由其子類按照不一樣加載Bean的邏輯各自實現;
依賴倒置原則:經過多級的抽象,提供了不一樣的接口,達到針對接口編程;

ClassPathXmlApplicationContext中調用層級父類上下文對象的構造後,再執行AbstractRefreshableConfigApplicationContext中實現的setConfigLocations設置上下文的Xml配置:

// 設置配置文件路徑
public void setConfigLocations(String... locations) {
	// 若是不爲空,則構早配置文件路徑,支持多個配置文件
	if (locations != null) {
		Assert.noNullElements(locations, "Config locations must not be null");
		this.configLocations = new String[locations.length];
		// 循環解析多個配置文件路徑
		for (int i = 0; i < locations.length; i++) {
			this.configLocations[i] = resolvePath(locations[i]).trim();
		}
	}
	else {
		this.configLocations = null;
	}
}

關於resolvePath的具體過程涉及到Spring IOC容器的環境Enviroment組件,具體的解析路徑的過程將在下章的探索Enviroment中詳解。

###總結 Spring上下文類型很是繁多,其中有直接面向各類場景直接使用的FileSystemApplicationContext、ClasspathXmlApplicationContext等等,還有不少實現基礎能力的上下文:

AbstractApplicationContext是上下文實現中的基石,其中定義了上下文Bean對象依賴注入的模板;

AbstractRefreshableApplicationContext也是很是重要的上下文,其中組合了BeanFactory和定義加載Bean的接口;

相關文章
相關標籤/搜索