探索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的接口;