1. 程式人生 > >Spring揭祕 讀書筆記 七 BeanFactory的啟動分析

Spring揭祕 讀書筆記 七 BeanFactory的啟動分析

首先,先看我自己畫的BeanFactory啟動時的時序圖。
第一次接觸時序圖,可能有些地方畫的不是很符合時序圖的規則,大家只關注呼叫順序即可。


public static  void main(String[] args){
       BeanFactory factory=new XmlBeanFactory(new ClassPathResource("applicationContext2.xml"));		
}

另外,上面的xmlbeanfactory已經被廢棄,改為

		Resource resource=new ClassPathResource("applicationContext2.xml");
		BeanFactory factory=new DefaultListableBeanFactory();
		BeanDefinitionReader bdr=new XmlBeanDefinitionReader((BeanDefinitionRegistry) factory);
		bdr.loadBeanDefinitions(resource);

但是處理邏輯沒怎麼變

我們從這一行程式碼開始。

關於Resources與ResourcesLoader的資料,大家可以看http://www.blogjava.net/DLevin/category/54913.html


public class XmlBeanFactory extends DefaultListableBeanFactory {
	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);	
	public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}
	
	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}
}
OK,我們之間開始看XmlBeanDefinitionReader的loadBeanDefinitions。(圖中的第二步)
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		        ....
			InputStream inputStream = encodedResource.getResource().getInputStream();
			InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			....
	}
上面的EncodedResource是對Resource的再包裝,不是重點。重點在doLoadBeanDefinitions
	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
			throws BeanDefinitionStoreException {
			Document doc = this.documentLoader.loadDocument(
					inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
			return registerBeanDefinitions(doc, resource);
		...
		//省略try catch
	}
上面的documentLoader是DefaultDocumentLoader的例項,Document是org.w3c.dom.Document的例項。
我們不用看具體的程式碼都知道,這一行就是把xml檔案讀入到Document中。 這是上圖中的第四步。
然後就是第五步registerBeanDefinitions。
	//XmlBeanDefinitionReader.java
	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
		documentReader.setEnvironment(this.getEnvironment());
		int countBefore = getRegistry().getBeanDefinitionCount();
		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
		return getRegistry().getBeanDefinitionCount() - countBefore;
	}
countBefore,就是之前已經註冊了多少bean。
這裡的重點在於:documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
而這個documentReader的預設實現是:DefaultBeanDefinitionDocumentReader
//DefaultBeanDefinitionDocumentReader.java
	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
		this.readerContext = readerContext;


		logger.debug("Loading bean definitions");
		Element root = doc.getDocumentElement();


		doRegisterBeanDefinitions(root);
	}
	protected void doRegisterBeanDefinitions(Element root) {
		
		this.delegate = createHelper(readerContext, root, parent);


		//preProcessXml與postProcessXml都是空方法
		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);


		this.delegate = parent;
	}
	protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	//<beans xmlns="http://www.springframework.org/schema/beans" />
	//檢視是否是預設的名稱空間
		if (delegate.isDefaultNamespace(root)) {
			NodeList nl = root.getChildNodes();
			for (int i = 0; i < nl.getLength(); i++) {
				Node node = nl.item(i);
				if (node instanceof Element) {
					Element ele = (Element) node;
					if (delegate.isDefaultNamespace(ele)) {
						//這一行用來出來bean標籤
						parseDefaultElement(ele, delegate);
					}
					else {
						delegate.parseCustomElement(ele);
					}
				}
			}
		}
		else {
			delegate.parseCustomElement(root);
		}
	}
我們看看我除錯時的一張圖

NodeList保持這所有bean的資訊。
parseDefaultElement(ele, delegate)這一行,就在for迴圈裡,不斷的處理每一個bean。
看到這,這個delegate是個什麼東西。
我給大家看看一下BeanDefinitionParserDelegate的幾個類變數。
	public static final String NAME_ATTRIBUTE = "name";
	public static final String BEAN_ELEMENT = "bean";
	public static final String META_ELEMENT = "meta";
	public static final String ID_ATTRIBUTE = "id";
	public static final String PARENT_ATTRIBUTE = "parent";
	public static final String CLASS_ATTRIBUTE = "class";
	public static final String ABSTRACT_ATTRIBUTE = "abstract";
	.....

猜都猜出來了:具體解析xml的。


這是圖中的第九步。從這之後都是迴圈處理每一個bean/import/alias/節點。
	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
		//是否是import節點
		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
			importBeanDefinitionResource(ele);
		}
		else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
			processAliasRegistration(ele);
		}
		//是否是bean節點
		else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
			processBeanDefinition(ele, delegate);
		}
		else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
			// recurse
			doRegisterBeanDefinitions(ele);
		}
	}
我們抓主要矛盾,看processBeanDefinition,這已經是圖中的第十步了。
大家看看上圖中第八步與第十步的名稱processBeanDefinitions與processBeanDefinition。就明白他們的關係了。
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);	
		// Register the final decorated instance.
		BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
在這裡我簡單介紹一下BeanDefinitionHolder
Bean----我們使用的各個基本元件。
BeanDefinition---定義Bean,我們可以認為是對bean的包裝。
BeanDefinitionHolder---持有BeanDefinition,我們可以認為是對BeanDefinition的包裝。
我們看parseBeanDefinitionElement。也就是上圖中的第11步。
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
		String id = ele.getAttribute(ID_ATTRIBUTE);
		String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
		String beanName = id;
		
		AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
		if (beanDefinition != null) {
			return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
		}


		return null;
	}
上面的程式碼,我已經刪去了一部分。(注意,上面的兩個parseBeanDefinitionElement方法,一個有兩個引數一個有三個引數)
還是parseBeanDefinitionElement,繼續解析。
第十一步:
//BeanDefinitionParserDelegate.java
public AbstractBeanDefinition parseBeanDefinitionElement(
			Element ele, String beanName, BeanDefinition containingBean) {

	        	String className = null;


			String parent = null;
			if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
				parent = ele.getAttribute(PARENT_ATTRIBUTE);
			}
			AbstractBeanDefinition bd = createBeanDefinition(className, parent);

			parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
			bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));

			parseMetaElements(ele, bd);
			parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
			parseReplacedMethodSubElements(ele, bd.getMethodOverrides());

			parseConstructorArgElements(ele, bd);
			parsePropertyElements(ele, bd);
			parseQualifierElements(ele, bd);


			bd.setResource(this.readerContext.getResource());
			bd.setSource(extractSource(ele));

			return bd;
	}
parseLookupOverrideSubElements,parseConstructorArgElements,parsePropertyElements大家能猜出來裡面是幹什麼的嗎?
bean元素下面還包含property標籤,lookup-method標籤,replaced-method標籤。
至於lookup-method標籤,replaced-method標籤裡面都是幹什麼的。大家自己百度。
至於parsePropertyElements,它是處理property。
更具體的,就是xml的分析了,沒有太多的技術點。我就不繼續講了。
咱們繼續往下走。


第十一步結束後,就是
//DefaultBeanDefinitionDocumentReader.java
	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	        //十一步
		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);	
		// Register the final decorated instance.
		//十二步
		BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}


	//BeanDefinitionReaderUtils.java
	public static void registerBeanDefinition(
			BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
			throws BeanDefinitionStoreException {


		// Register bean definition under primary name.
		String beanName = definitionHolder.getBeanName();
		registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());		
	}
大家得記得,我們的容器,XmlBeanFactory本身也實現了BeanDefinitionRegistry。
所以registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
呼叫的就是XmlBeanFactory的registerBeanDefinition(XmlBeanFactory的父類DefaultListableBeanFactory就已經實現了registerBeanDefinition這個方法了)。
	//DefaultListableBeanFactory.java
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {
			this.beanDefinitionNames.add(beanName);
			
			this.beanDefinitionMap.put(beanName, beanDefinition);
			resetBeanDefinition(beanName);
		
	}
beanDefinitionMap裡面維護著整個容器裡面的bean。
大功告成。

接著我們看


感謝glt




相關推薦

Spring揭祕 讀書筆記 BeanFactory啟動分析

首先,先看我自己畫的BeanFactory啟動時的時序圖。 第一次接觸時序圖,可能有些地方畫的不是很符合時序圖的規則,大家只關注呼叫順序即可。public static void main(String[] args){ BeanFactory factor

spring揭祕 讀書筆記BeanFactory的物件註冊與依賴繫結

本文是王福強所著<<spring揭祕>>一書的讀書筆記 我們前面就說過,Spring的IoC容器時一個IoC Service Provider,而且IoC Service Provider提供兩個功能物件的建立,依賴關係的管理。 不過,IoC容器這個

Spring揭祕讀書筆記 八 資料訪問異常體系

這篇部落格 來自spring揭祕一書的第十三章 為什麼要有訪問異常都有一個體系,這個我們得從DAO模式說起。 DAO模式 任何一個系統,不管是一個最簡單的小系統,還是大規模的系統,都得跟資料打交道,說白了都得時常進行存取資料的操作。我們暫且不論資料本身,資料儲存的方式就已經

spring揭秘 讀書筆記BeanFactory的對象註冊與依賴綁定

oct anr ctx nfc col line 增加 sso default 本文是王福強所著<<spring揭秘>>一書的讀書筆記 我們前面就說過,Spring的IoC容器時一個IoC Service Provider,並且IoC

《構建之法》讀書筆記

而不是 發布 目的 執行 等等 出現 壓力 交付 無法 計算機領域有很多基本名詞,比如說最常出現的,程序員都不太喜歡的——bug(缺陷)。 測試設計有兩類方法:黑箱(Black Box)和白箱(White Box)。要註意的是,這是軟件測試設計的方法,不是軟

how tomcat works讀書筆記 日誌記錄器

錯誤輸出 new t rac .net ase code tor apache 格式 大家能夠松一口氣了,這個組件比較簡單,這一節和前面幾節想比,也簡單的多。 Logger接口 Tomcat中的日誌記錄器都必須實現org.apache.catalina.Logger接

[讀書筆記] Python數據分析 (一) 準備工作

基礎 htm 環境 防止 功能 多維 處理工具 ati 增強 1. python中數據結構:矩陣,數組,數據框,通過關鍵列相互聯系的多個表(SQL主鍵,外鍵),時間序列 2. python 解釋型語言,程序員時間和CPU時間衡量,高頻交易系統 3. 全局解釋器鎖GIL,

《算法導論》讀書筆記()

背包問題 ostream 根據 選擇性 pos 畢業論文 結構 size 所有   前言:貪心算法也是用來解決最優化問題,將一個問題分成子問題,在現在子問題最優解的時,選擇當前看起來是最優的解,期望通過所做的局部最優選擇來產生一個全局最優解。書中先從活動選擇問題來引入貪

讀書筆記】深入淺出資料分析

目錄  · · · · · · 1 資料分析引言:分解資料 1 2 實驗:檢驗你的理論 37 3 最優化:尋找最大值 75 4 資料圖形化:圖形讓你更精明 111 5 假設檢驗:假設並非如此

文字上的演算法讀書筆記--理解語言有多難

理解語言有多難 7.1 自然語言處理 自然語言處理NLP是讓計算機能處理語言,20世紀50年代,大家關心的是人類學習語言的認知研究上,計算機處理語言,必須先分析語句和獲取語義,需要分析詞的次序,句子的句法規則,就是喬姆斯基的有限狀態自動機刻畫語言的語法,建立了自然語言的有限狀態模型,這時是基

風火程式設計--《python核心程式設計》讀書筆記()

python核心程式設計–第二版 第十八章 18.2程序和執行緒 每個程序都有獨立的地址空間,記憶體, 資料站和其他記錄其執行軌跡的輔助資料, 所以只能用程序間通訊IPC 每個執行緒都有獨立的指令指標, 記錄執行到的位置 可以通過thread.exit()退出執行緒 start_

java效能調優權威指南讀書筆記(延遲調優)

延遲調優 這一步調優的目的是達到程式的延遲性需求,其中的手段有優化java堆的大小的配置,不同垃圾收集器的切換 在這裡我們的延遲調優指的是最大延遲時間,所以以這個標準為目的我們在調優的時候需要減少每次垃圾收集的時間,這就需要我們的垃圾收集需要使用高次數低停頓的策略 所以我們

讀書筆記】《深入分析Java Web技術內幕》

剛拿到書,感覺寫的很不錯,越看到後面越質疑我之前的觀點。此書寫的面的確很全,可謂面面俱到,但是面面俱到的反面就是都不深入,與之標題相差甚遠,《淺析Java Web技術內幕》更合適。 縱觀全書,本書更適合初級Java開發人員對JavaWeb做進一步的瞭解,作者對

讀書筆記_金融資料分析 | 金融資料及其特徵

金融資料分析導論——基於R語言 機械工業出版社 作者:Ruey S. Tsay [美] 芝加哥大學 資產收益率 大多數金融研究都是針對資產收益率,而不是資產價格。 Campbell等(1997)給出了使用資產收益率的兩個主要原因

android_wifi讀書筆記之5-WPA_SUPPLICANT分析

本文為讀書筆記,整理自網路文獻和原始碼 5 WPA_SUPPLICANT分析 5.1、WPA_SUPPLICANT 分析1: 參考文獻: http://www.cnblogs.com/chenbin7/p/3266032.html http://blog.chinaunix

AspectJ形式的Pointcut--spring揭祕學習筆記

@AspectJ形式的Pointcut的宣告包含如下兩個部分: Pointcut Expression:其載體為@Pointcut,該註解是方法級別的註解,所以Pointcut Expression不能脫離某個方法單獨宣告,被附著的的方法的名稱是Pointc

Spring揭祕 學習筆記一 (Spring的IoC容器 二)

4.3.5 bean的 scope scope可理解為“作用域”。 scope用來宣告容器中的物件所應該處的限定場景或者說該物件的存活時間,即容器在物件進入其相應的scope之前,生成並裝配這些物件,在該物件不再處於這些scope的限定之後,容器通常會銷燬這

Spring揭祕讀書筆記四:統一事務管理

一、背景和支撐 事務程式碼混在業務程式碼之中,不便於維護。基於上一章介紹的 AOP可以想到,通過AOP的方式 將 Spring事務從 業務程式碼中剝離出來。  自己實現的話:宣告一個註解,然後通過 AOP的  @annoation的 Pointcut 為 帶有 自定義註解

Spring Boot揭祕:快速構建微服務體系》讀書筆記

第一章 瞭解微服務 火車模型:交付的服務就像一輛火車,這個服務相關的所有功能對應的專案成果,就是要裝上火車車廂的一件件貨物,交付的列車只有等到所有專案都開發測試完成之後才可以裝車觸發,完成這個服務的交付。 微服務的益處:獨立,進而可擴充套件性;多語言生態;

《大型網站技術架構:核心原理與案例分析》-- 讀書筆記 (5) :網購秒殺系統

案例 並發 刷新 隨機 url 對策 -- 技術 動態生成 1. 秒殺活動的技術挑戰及應對策略 1.1 對現有網站業務造成沖擊 秒殺活動具有時間短,並發訪問量大的特點,必然會對現有業務造成沖擊。對策:秒殺系統獨立部署 1.2 高並發下的應用、