1. 程式人生 > >Spring 原始碼分析(二) —— 核心容器

Spring 原始碼分析(二) —— 核心容器

容器概述

      IoC也被稱作依賴注入(DI)。它是一個處理物件依賴項的過程,也就是將他們一起工作的其他的物件,只有通過構造引數、工廠方法引數或者(屬性注入)通過構造引數例項化或通過工廠方法返回物件後再設定屬性。當建立bean後,IoC容器再將這些依賴項注入進去。這個過程基本上是反轉的,因此得名控制反轉(IoC)。

     下圖是 IoC 的高級別檢視

        IoC容器利用Java的POJO類和配置元資料來生成 完全配置和可執行 的系統或應用程式。而Bean在Spring中就是POJO,也可以認為Bean就是物件。

設計實現

    介面設計

           Spring作為面向物件程式設計的集大成之作,我們直接從介面入手可以幫助我們更直觀的瞭解Ioc容器的設計原理。

下圖描述了Ioc容器中的主要介面設計

     體實現體系,比如DefaultListableBeanFactory就是為了實現ConfigurableBeanFactory,從而成為一個簡單Ioc容器實現。與其他Ioc容器類似,XmlBeanFactory就是為了實現BeanFactory,但都是基於DefaultListableBeanFactory的基礎做了擴充套件。同樣的,ApplicationContext也一樣。        

        從圖中我們可以簡要的做出以下分析:

        1.從介面BeanFactory到HierarchicalBeanFactory,再到ConfigurableBeanFactory,這是一條主要的BeanFactory設計路徑。在這條介面設計路徑中,BeanFactory介面定義了基本的Ioc容器的規範。在這個介面定義中,包括了getBean()這樣的Ioc容器的基本方法(通過這個方法可以從容器中取得Bean)。而HierarchicalBeanFactory介面在繼承了BeanFactory的基本介面後,增加了getParentBeanFactory()的介面功能,使BeanFactory具備了雙親Ioc容器的管理功能。在接下來的ConfigurableBeanFactory介面中,主要定義了一些對BeanFactory的配置功能,比如通過setParentBeanFactory()設定雙親Ioc容器,通過addBeanPostProcessor()配置Bean後置處理器,等等。通過這些介面設計的疊加,定義了BeanFactory就是最簡單的Ioc容器的基本功能。

        2.第二條介面設計主線是,以ApplicationContext作為核心的介面設計,這裡涉及的主要介面設計有,從BeanFactory到ListableBeanFactory,再到ApplicationContext,再到我們常用的WebApplicationContext或者ConfigurableApplicationContext介面。我們常用的應用基本都是org.framework.context 包裡的WebApplicationContext或者ConfigurableApplicationContext實現。在這個介面體現中,ListableBeanFactory和HierarchicalBeanFactory兩個介面,連線BeanFactory介面定義和ApplicationContext應用的介面定義。在ListableBeanFactory介面中,細化了許多BeanFactory的介面功能,比如定義了getBeanDefinitionNames()介面方法;對於ApplicationContext介面,它通過繼承MessageSource、ResourceLoader、ApplicationEventPublisher介面,在BeanFactory簡單Ioc容器的基礎上添加了許多對高階容器的特性支援。

       3.這個介面系統是以BeanFactory和ApplicationContext為核心設計的,而BeanFactory是Ioc容器中最基本的介面,在ApplicationContext的設計中,一方面,可以看到它繼承了BeanFactory介面體系中的ListableBeanFactory、AutowireCapableBeanFactory、HierarchicalBeanFactory等BeanFactory的介面,具備了BeanFactory Ioc容器的基本功能;另一方面,通過繼承MessageSource、ResourceLoadr、ApplicationEventPublisher這些介面,BeanFactory為ApplicationContext賦予了更高階的Ioc容器特性。對於ApplicationContext而言,為了在Web環境中使用它,還設計了WebApplicationContext介面,而這個介面通過繼承ThemeSource介面來擴充功能。

    BeanFactory容器的設計

        恩,我們與其寫繁瑣的文字,不如直接閱讀程式碼來的直接的多。

最原始的容器:BeanFactory

package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.core.ResolvableType;

/**
 * BeanFactory作為最原始同時也最重要的Ioc容器,它主要的功能是為依賴注入 (DI) 提供支援,
 * BeanFactory 和相關的介面,比如,BeanFactoryAware、 DisposableBean、InitializingBean,
 * 仍舊保留在 Spring 中,主要目的是向後相容已經存在的和那些 Spring 整合在一起的第三方框架。在 
 * Spring 中,有大量對 BeanFactory 介面的實現。其中,最常被使用的是 XmlBeanFactory 類。
 * 這個容器從一個 XML 檔案中讀取配置元資料,由這些元資料來生成一個被配置化的系統或者應用。
 * 在資源寶貴的移動裝置或者基於applet的應用當中, BeanFactory 會被優先選擇。否則,一般使用的是 
 * ApplicationContext.
 * 這裡定義的只是一系列的介面方法,通過這一系列的BeanFactory介面,可以使用不同的Bean的檢索方法很 
 * 方便地從Ioc容器中得到需要的Bean,從而忽略具體
 * 的Ioc容器的實現,從這個角度上看,這些檢索方法代表的是最為基本的容器入口。
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 13 April 2001
 */
public interface BeanFactory {

	/**
	 * 轉定義符"&" 用來引用例項,或把它和工廠產生的Bean區分開,就是說,如果一個FactoryBean的名 
     * 字為a,那麼,&a會得到那個Factory
	 *
	 * FactoryBean和BeanFactory 是在Spring中使用最為頻繁的類,它們在拼寫上很相似。一個是 
     * Factory,也就是Ioc容器或物件工廠;一個是Bean。在Spring中,所有的Bean都是由 
     * BeanFactory(也就是Ioc容器)來進行管理的。但對 
     * FactoryBean而言,這個Bean不是簡單的Bean,而是一個能產生或者修飾物件生成的工廠Bean,它的 
     * 實現與設計模式中的工廠模式和修飾器模式類似。
	 */
	String FACTORY_BEAN_PREFIX = "&";

	/**
	 * 五個不同形式的getBean方法,獲取例項
	 * @param name 檢索所用的Bean名
	 * @return Object(<T> T) 例項物件
	 * @throws BeansException 如果Bean不能取得
	 */
	Object getBean(String name) throws BeansException;
	<T> T getBean(String name, Class<T> requiredType) throws BeansException;
	<T> T getBean(Class<T> requiredType) throws BeansException;
	Object getBean(String name, Object... args) throws BeansException;
	<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

	/**
	 * 讓使用者判斷容器是否含有指定名字的Bean.
	 * @param name 搜尋所用的Bean名
	 * @return boolean 是否包含其中
	 */
	boolean containsBean(String name);

	/**
	 * 查詢指定名字的Bean是否是Singleton型別的Bean.
	 * 對於Singleton屬性,可以在BeanDefinition指定.
	 * @param name 搜尋所用的Bean名
	 * @return boolean 是否包是Singleton
	 * @throws NoSuchBeanDefinitionException 沒有找到Bean
	 */
	boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

	/**
	 * 查詢指定名字的Bean是否是Prototype型別的。
	 * 與Singleton屬性一樣,可以在BeanDefinition指定.
	 * @param name 搜尋所用的Bean名
	 * @return boolean 是否包是Prototype
	 * @throws NoSuchBeanDefinitionException 沒有找到Bean
	 */
	boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

	/**
	 * 查詢指定了名字的Bean的Class型別是否是特定的Class型別.
	 * @param name 搜尋所用的Bean名
	 * @param typeToMatch 匹配型別
	 * @return boolean 是否是特定型別
	 * @throws NoSuchBeanDefinitionException 沒有找到Bean
	 */
	boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
	boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

	/**
	 * 查詢指定名字的Bean的Class型別.
	 * @param name 搜尋所用的Bean名
	 * @return 指定的Bean或者null(沒有找到合適的Bean)
	 * @throws NoSuchBeanDefinitionException 沒有找到Bean
	 */
	Class<?> getType(String name) throws NoSuchBeanDefinitionException;

	/**
	 * 查詢指定了名字的Bean的所有別名,這些都是在BeanDefinition中定義的
	 * @param name 搜尋所用的Bean名
	 * @return 指定名字的Bean的所有別名 或者一個空的陣列
	 */
	String[] getAliases(String name);
}

容器的基礎:XmlBeanFactory

package org.springframework.beans.factory.xml;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.io.Resource;

/**
 * XmlBeanFactory是BeanFactory的最簡單實現類
 * 
 * XmlBeanFactory的功能是建立在DefaultListableBeanFactory這個基本容器的基礎上的,
 * 並在這個基本容器的基礎上實行了其他諸如XML讀取的附加功能。XmlBeanFactory使用了 
 * DefaultListableBeanFactory作為基礎類,DefaultListableBeanFactory是一個很重
 * 要的Ioc實現,會在下一章進行重點論述。
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Chris Beams
 * @since 15 April 2001
 */
public class XmlBeanFactory extends DefaultListableBeanFactory {
	
	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);

	/**
	 * 根據給定來源,建立一個XmlBeanFactory
	 * @param resource  Spring中對與外部資源的抽象,最常見的是對檔案的抽象,特別是XML檔案。而且Resource裡面通常
	 * 是儲存了Spring使用者的Bean定義,比如applicationContext.xml在被載入時,就會被抽象為Resource來處理。
	 * @throws BeansException 載入或者解析中發生錯誤
	 */
	public XmlBeanFactory(Resource resource) throws BeansException {
		this(resource, null);
	}

	/**
	 * 根據給定來源和BeanFactory,建立一個XmlBeanFactory
	 * @param resource  Spring中對與外部資源的抽象,最常見的是對檔案的抽象,特別是XML檔案。而且Resource裡面通常
	 * 是儲存了Spring使用者的Bean定義,比如applicationContext.xml在被載入時,就會被抽象為Resource來處理。
	 * @param parentBeanFactory 父類的BeanFactory
	 * @throws BeansException 載入或者解析中發生錯誤
	 */
	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}
}

最原始Ioc容器的使用

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

/**
 * 最原始的Ioc容器使用,當然這也是Spring容器中效率最高的用法,比起繁瑣的文字,閱讀原始碼來得直觀得多。
 * 只需要寫兩行程式碼就行了,當然前提是要準備好Spring的配置檔案
 *
 * @author  kay
 * @since   1.0
 */
@SuppressWarnings("deprecation")
public class SimpleBeanFactory {
	public static void main(String[] args) {
		ClassPathResource resource = new ClassPathResource("applicationContext.xml");
		BeanFactory beanFactory = new XmlBeanFactory(resource);		
		Message message = beanFactory.getBean("message", Message.class);    //Message是自己寫的測試類
		message.printMessage();
	}
}

下面是XmlBeanFactory在使用過程中涉及到的類的關係圖

       圖中空心三角加實線代表繼承、空心三角加虛線代表實現、實線箭頭加虛線代表依賴、實心菱形加實線代表組合。這裡用下劃線代表介面,沒有下劃線的代表類。

        看著非常複雜是吧,不要緊,我們以程式碼來做簡要說明

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;

/**
 * 這是與SimpleBeanFactory等效的程式設計式使用Ioc容器
 * 
 * 從中我也可以看到一些Ioc的基本原理,同時也揭示了Ioc實現中的一些關鍵類:如Resource、DefaultListableBeanFactory
 * 以及BeanDefinitionReader等等
 *
 * @author  kay
 * @since   1.0
 */
public class ProgramBeanFactory{
	public static void main(String[] args) {
		ClassPathResource resource = new ClassPathResource("applicationContext.xml");
		DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
		XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
		reader.loadBeanDefinitions(resource);
		Message message = factory.getBean("message", Message.class);	//Message是自己寫的測試類
		message.printMessage();
	}
}

        以上,可以簡單說明我們在使用Ioc容器時,需要如下幾個步驟:

           1,建立Ioc配置檔案的抽象資源,這個抽象資源包含了BeanDefinition的定義資訊。

            2,建立一個BeanFactory,這裡使用了DefaultListableBeanFactory。

            3,建立一個載入BeanDefinition的讀取器,這裡使用XmlBeanDefinitionReader來載入XML檔案形式的BeanDefinition,通過一個回撥配置給BeanFactory。

            4,從定義好的資源位置讀入配置資訊,具體的解析過程由XmlBeanDefinitionReader來完成。完成整個載入和註冊Bean定義之後,需要的Ioc容器就建立起來了。這個時候我們就可以直接使用Ioc容器了。

        恩,以下是Bean在使用過程中的解析、註冊時效圖,我們來一步一步分析,它是怎麼在原始碼中實現的。

配置檔案封裝類:ClassPathResource

在Java中,將不同來源的資源抽象成URL,通過註冊不同的handler(URLStreamHandler)來處理不同來源間的資源讀取邏輯。而URL中卻沒有提供一些基本方法來實現自己的抽象結構。因而Spring對其內部資源,使用了自己的抽象結構:Resource介面來封裝。而ClassPathResource實現類即是對Resource的實現。

Resource介面體系

         資源的原始介面為Resource,它繼承自InputStreamResource,實現了其getInstream方法,這樣所有的資源就是通過該方法來獲取輸入流的。對於資源的載入,也實現了統一,定義了一個資源載入頂級介面ResourceLoader,它預設的載入就是DefaultResourceLoader。

InputStreamSource介面

package org.springframework.core.io;

import java.io.IOException;
import java.io.InputStream;

/**
 * InputStreamSource 封裝任何能返回InputStream的類,比如File、Classpath下的資源和Byte Array等
 *
 * @author Juergen Hoeller
 * @since 20.01.2004
 */
public interface InputStreamSource {
	
	/**
	 * 返回InputStream的類,比如File、Classpath下的資源和Byte Array等
	 * @return InputStream 返回一個新的InputStream的物件
	 * @throws IOException 如果資源不能開啟則丟擲異常
	 */
	InputStream getInputStream() throws IOException;
}

Resource介面

package org.springframework.core.io;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;

/**
 * Resource介面抽象了所有Spring內部使用到的底層資源:File、URL、Classpath等。
 * 同時,對於來源不同的資原始檔,Resource也有不同實現:檔案(FileSystemResource)、Classpath資源(ClassPathResource)、
 * URL資源(UrlResource)、InputStream資源(InputStreamResource)、Byte陣列(ByteArrayResource)等等。
 *
 * @author Juergen Hoeller
 * @since 28.12.2003
 */
public interface Resource extends InputStreamSource {

	/**
	 * 判斷資源是否存在
	 * @return boolean 是否存在
	 */
	boolean exists();

	/**
	 * 判斷資源是否可讀
	 * @return boolean 是否可讀
	 */
	boolean isReadable();

	/**
	 * 是否處於開啟狀態
	 * @return boolean 是否開啟
	 */
	boolean isOpen();

	/**
	 * 得到URL型別資源,用於資源轉換
	 * @return URL 得到URL型別
	 * @throws IOException 如果資源不能開啟則丟擲異常
	 */
	URL getURL() throws IOException;

	/**
	 * 得到URI型別資源,用於資源轉換
	 * @return URI 得到URI型別
	 * @throws IOException 如果資源不能開啟則丟擲異常
	 */
	URI getURI() throws IOException;

	/**
	 * 得到File型別資源,用於資源轉換
	 * @return File 得到File型別
	 * @throws IOException 如果資源不能開啟則丟擲異常
	 */
	File getFile() throws IOException;

	/**
	 * 獲取資源長度
	 * @return long 資源長度
	 * @throws IOException 如果資源不能開啟則丟擲異常
	 */
	long contentLength() throws IOException;

	/**
	 * 獲取lastModified屬性
	 * @return long 獲取lastModified
	 * @throws IOException 如果資源不能開啟則丟擲異常
	 */
	long lastModified() throws IOException;

	/**
	 * 建立一個相對的資源方法
	 * @param relativePath 相對路徑
	 * @return Resource 返回一個新的資源
	 * @throws IOException 如果資源不能開啟則丟擲異常
	 */
	Resource createRelative(String relativePath) throws IOException;

	/**
	 * 獲取檔名稱
	 * @return String 檔名稱或者null
	 */
	String getFilename();

	/**
	 * 得到錯誤處理資訊,主要用於錯誤處理的資訊列印
	 * @return String 錯誤資源資訊
	 */
	String getDescription();
}

根據上面的推論,我們可以理解為這兩段程式碼,在某種程度來說是完全等效的

ClassPathResource resource = new ClassPathResource("applicationContext.xml");
InputStream inputStream = resource.getInputStream();
Resource resource = new ClassPathResource("applicationContext.xml");
InputStream inputStream = resource.getInputStream();

這樣得到InputStream以後,我們可以拿來用了。值得一提是,不同的實現有不同的呼叫方法,這裡就不展開了。下面是ClassPathResource的具體實現:

ClassPathResource.java

private final String path;

private ClassLoader classLoader;

public ClassPathResource(String path) {
        this(path, (ClassLoader) null);    //這裡是入口,直接跳入下面的ClassPathResource(String path, ClassLoader classLoader) 中
}

public ClassPathResource(String path, ClassLoader classLoader) {
        Assert.notNull(path, "Path must not be null");
        String pathToUse = StringUtils.cleanPath(path);
        if (pathToUse.startsWith("/")) {
                pathToUse = pathToUse.substring(1);
        }
        this.path = pathToUse;
        this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}

忽略給定介面:DefaultListableBeanFactory

        這裡 DefaultListableBeanFactory 所起到的是忽略給定介面自動裝配功能。簡單來說,一般 bean 中的功能 A 如果沒有初始化,那麼Spring會自動初始化A,這是Spring的一個特性。但當某些特殊情況時,B不會初始化,比如:B已經實現了 BeanNameAware介面。可以說,就是通過其他方式來解析依賴,類似於 BeanFactory 的 BeanFactoryAware。下面是具體實現:

DefaultListableBeanFactory.java

public DefaultListableBeanFactory() {
        super();    //直接指向下面 AbstractAutowireCapableBeanFactory()
}

AbstractAutowireCapableBeanFactory.java

private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<Class<?>>();

public AbstractAutowireCapableBeanFactory() {
        super();
        ignoreDependencyInterface(BeanNameAware.class);    //忽略給定介面自動裝配功能的主要實現處
        ignoreDependencyInterface(BeanFactoryAware.class);
        ignoreDependencyInterface(BeanClassLoaderAware.class);
}

public void ignoreDependencyInterface(Class<?> ifc) {
        this.ignoredDependencyInterfaces.add(ifc);
}

BeanDefinition的載入、解析和註冊:XmlBeanDefinitionReader

         這裡是BeanDefinition真正被載入的地方。這個載入過程就是把使用者定義好的Bean表示成Ioc容器內部的資料結構,當然這個資料結構就是BeanDefinition。而BeanDefinition實際上就是POJO物件在Ioc容器中的抽象,通過這個BeanDefinition定義的資料結構,讓Ioc容器能夠對POJO物件也就是Bean進行管理。

XmlBeanDefinitionReader.java

public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        //loadBeanDefinitions的具體實現,而EncodedResource主要用於對資原始檔的處理,
        //而其主要實現方法getReader()在下面有所介紹
        return loadBeanDefinitions(new EncodedResource(resource));    
}

public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        if (logger.isInfoEnabled()) {
                logger.info("Loading XML bean definitions from " + encodedResource.getResource());
        }

        //通過屬性來記錄已經載入的資源
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
                currentResources = new HashSet<EncodedResource>(4);
                this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        if (!currentResources.add(encodedResource)) {
                throw new BeanDefinitionStoreException(
                        "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
	
        // 呼叫DefaultResourceLoader的getResources方法完成具體的Resource定位 
        try {
                //從EncodedResource中獲取已經封裝的Resource物件並再次從Resource中獲取inputStream 
                InputStream inputStream = encodedResource.getResource().getInputStream();
                try {
                        InputSource inputSource = new InputSource(inputStream);
                        if (encodedResource.getEncoding() != null) {
                                inputSource.setEncoding(encodedResource.getEncoding());
                        }
                        return doLoadBeanDefinitions(inputSource, encodedResource.getResource());    //真正的邏輯核心
                }
                finally {
                        inputStream.close();    //關閉inputStream
                }
        }
        catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                                "IOException parsing XML document from " + encodedResource.getResource(), ex);
        }
        finally {
                currentResources.remove(encodedResource);
                if (currentResources.isEmpty()) {
                        this.resourcesCurrentlyBeingLoaded.remove();
                }
        }
}

/**
 * 真正的核心處理部分
 * 如果不考慮冗餘的程式碼,其實只做三件事:
 *  1.獲取XML檔案的驗證模式
 *  2.載入XML,並獲取Document.
 *  3.返回的Document,註冊Bean
 */
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
                // 取得XML檔案的Document物件, 這個解析過程由DefaultDocumentLoader完成  
                Document doc = doLoadDocument(inputSource, resource);    
                // 啟動對BeanDefinition解析的詳細過程, 解析過程中會使用到Spring的Bean配置規則
                return registerBeanDefinitions(doc, resource);
        }
        catch (BeanDefinitionStoreException ex) {
                throw ex;
        }
        catch (SAXParseException ex) {
                throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                                "Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
        }
        catch (SAXException ex) {
                throw new XmlBeanDefinitionStoreException(resource.getDescription(),
                                "XML document from " + resource + " is invalid", ex);
        }
        catch (ParserConfigurationException ex) {
                throw new BeanDefinitionStoreException(resource.getDescription(),
                                "Parser configuration exception parsing XML from " + resource, ex);
        }
        catch (IOException ex) {
                throw new BeanDefinitionStoreException(resource.getDescription(),
                                "IOException parsing XML document from " + resource, ex);
        }
        catch (Throwable ex) {
                throw new BeanDefinitionStoreException(resource.getDescription(),
                                "Unexpected exception parsing XML document from " + resource, ex);
        }
}

protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        //loadDocument直接用於註冊Document,getValidationModeForResource方法作用於XML的載入
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
}

EncodedResource.java

public Reader getReader() throws IOException {
        if (this.charset != null) {
                return new InputStreamReader(this.resource.getInputStream(), this.charset);
        }
        else if (this.encoding != null) {
                return new InputStreamReader(this.resource.getInputStream(), this.encoding);
        }
        else {
                return new InputStreamReader(this.resource.getInputStream());
        }
}

XML檔案驗證

        獲取XML檔案的驗證模式,一般分為兩步:首先,是XML檔案驗證,然後,就是驗證模式的讀取。

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans
	xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">    //XSD方式,用於驗證XML檔案的正確性

	<bean id="..">...</bean>
</beans>

        常用的XML檔案驗證方法有兩種:DTD和XSD,DTD現在基本不用了,而上圖中的spring-beans-4.0.xsd對應在原始碼中的org.springframework.beans.factory.xml.spring-beans-4.0.xsd。這裡就不具體介紹了,有興趣可以自己去研究。

載入XML

         Spring中通過getValidationModeForResource方法來獲取上面講的DTD和XSD驗證方法,不過就程式設計而言,還是非常簡單的。只是要特別需要注意的是自動檢測驗證模式的實現。

XmlBeanDefinitionReader.java

public static final int VALIDATION_AUTO = XmlValidationModeDetector.VALIDATION_AUTO;

public static final int VALIDATION_XSD = XmlValidationModeDetector.VALIDATION_XSD;

protected int getValidationModeForResource(Resource resource) {
        int validationModeToUse = getValidationMode();
        //如果手動指定了驗證模式則使用指定的驗證模式
        if (validationModeToUse != VALIDATION_AUTO) {
                return validationModeToUse;
        }
        //如果沒有指定,則自動檢測
        int detectedMode = detectValidationMode(resource);    //自動檢測主要是在detectValidationMode(Resource resource)完成的
        if (detectedMode != VALIDATION_AUTO) {
                return detectedMode;
        }
        return VALIDATION_XSD;
}

protected int detectValidationMode(Resource resource) {
        if (resource.isOpen()) {
                throw new BeanDefinitionStoreException(
                        "Passed-in Resource [" + resource + "] contains an open stream: " +
                        "cannot determine validation mode automatically. Either pass in a Resource " +
                        "that is able to create fresh streams, or explicitly specify the validationMode " +
                        "on your XmlBeanDefinitionReader instance.");
        }

        InputStream inputStream;
        try {
                inputStream = resource.getInputStream();
        }
        catch (IOException ex) {
                throw new BeanDefinitionStoreException(
                        "Unable to determine validation mode for [" + resource + "]: cannot open InputStream. " +
                        "Did you attempt to load directly from a SAX InputSource without specifying the " +
                        "validationMode on your XmlBeanDefinitionReader instance?", ex);
        }

        try {
                return this.validationModeDetector.detectValidationMode(inputStream);    //自動檢測則是給detectValidationMode完成的
        }
        catch (IOException ex) {
                throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
                        resource + "]: an error occurred whilst reading from the InputStream.", ex);
        }
}

XmlValidationModeDetector.java

public static final int VALIDATION_NONE = 0;

public static final int VALIDATION_AUTO = 1;

public static final int VALIDATION_DTD = 2;

public static final int VALIDATION_XSD = 3;

private static final String DOCTYPE = "DOCTYPE";

public int detectValidationMode(InputStream inputStream) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        try {
                boolean isDtdValidated = false;
                String content;
                while ((content = reader.readLine()) != null) {
                        content = consumeCommentTokens(content);
                        //空或註釋略過
                        if (this.inComment || !StringUtils.hasText(content)) {
                                continue;
                        }
                        if (hasDoctype(content)) {
                                isDtdValidated = true;
                                break;
                        }
                        //讀取<前的資訊
                        if (hasOpeningTag(content)) {
                                break;
                        }
                }
                return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
        }
        catch (CharConversionException ex) {
                return VALIDATION_AUTO;
        }
        finally {
                reader.close();
        }
}

private boolean hasDoctype(String content) {
        return content.contains(DOCTYPE);
}

private boolean hasOpeningTag(String content) {
        if (this.inComment) {
                return false;
        }
        int openTagIndex = content.indexOf('<');
        return (openTagIndex > -1 && (content.length() > openTagIndex + 1) &&
                Character.isLetter(content.charAt(openTagIndex + 1)));
}

獲取Document

         通過以上的驗證準備,就可以進行載入了,而XmlBeanDefinitionReader並沒有自己去完成,而是給了DocumentLoader介面去完成的,而他呼叫的是DefaultDocumentLoader.loadDocument方法。loadDocument沒有太多可描述的。所以。。。

XmlBeanDefinitionReader.java

private DocumentLoader documentLoader = new DefaultDocumentLoader();

//EntityResolver主要用於處理前面提到的DTD方法的處理
protected EntityResolver getEntityResolver() {
        if (this.entityResolver == null) {
                ResourceLoader resourceLoader = getResourceLoader();
                if (resourceLoader != null) {
                        this.entityResolver = new ResourceEntityResolver(resourceLoader);
                }
                else {
                        this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
                }
        }
        return this.entityResolver;
}

DefaultDocumentLoader.java

public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
                ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {

        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isDebugEnabled()) {
                logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        return builder.parse(inputSource);
}

BeanDefinition的解析

         BeanDefinition載入過程其實就是把定義的BeanDefinition在IoC容器中轉化為一個Spring內部表示的資料結構的過程。IoC容器對Bean的管理和依賴注入的實現,都是通過對其持有的BeanDefinition進行各種相關的操作來完成的。這些BeanDefinition資料在IoC容器中通過一個HashMap來維護。

XmlBeanDefinitionReader.java

private Class<?> documentReaderClass = DefaultBeanDefinitionDocumentReader.class;   

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        // 得到BeanDefinitionDocumentReader來對XML的BeanDefinition進行解析 
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        
        // 具體的解析過程在BeanDefinitionDocumentReader的registerBeanDefinitions方法中完成
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;    
}

protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
        return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));    
}

DefaultBeanDefinitionDocumentReader.java

public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;

public static final String NESTED_BEANS_ELEMENT = "beans";

public static final String ALIAS_ELEMENT = "alias";

public static final String ALIAS_ATTRIBUTE = "alias";

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();    // 獲得Document的根元素
        doRegisterBeanDefinitions(root);
}

protected void doRegisterBeanDefinitions(Element root) {
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
                String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
                if (StringUtils.hasText(profileSpec)) {
                        String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                                profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                        if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                                return;
                        }
                }
        }

        preProcessXml(root);    // 解析Bean定義之前, 增強解析過程的可擴充套件性 
        parseBeanDefinitions(root, this.delegate);    // 從Document的根元素開始進行Bean定義的Document物件  
        postProcessXml(root);    // 解析Bean定義之前, 增強解析過程的可擴充套件性  

        this.delegate = parent;
}

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        if (delegate.isDefaultNamespace(root)) {
                NodeList nl = root.getChildNodes();    // 獲取Document物件根元素的所有子節點並迴圈解析  
                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)) {
                                        parseDefaultElement(ele, delegate);    // 解析Spring的Bean規則預設元素節點 
                                }
                                else {
                                        delegate.parseCustomElement(ele);    // 解析自定義元素節點 
                                }
                        }
                }
        }
        else {
                delegate.parseCustomElement(root);    // 解析自定義元素根節點  
        }
}

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {    // 解析import元素
                importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {    // 解析alias元素
                processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {    // 解析bean元素
                processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {    // 解析內嵌beans元素, 作為根節點遞迴解析
                doRegisterBeanDefinitions(ele);
        }
}

         恩,直接從程式碼就可以看出,Spring首先獲取Document的根元素(一般為<beans/>),然後取得根元素所有的子節點並迴圈解析這些子節點;如果子節點在Spring預設的名稱空間內,則按照Spring Bean定義規則來解析,否則按照自定義的節點解析。在按照Spring Bean定義規則進行解析的parseDefaultElement方法中,完成了對<import/>、<alias/>、<bean/>、<beans/>等元素的解析。

        為了使文章簡短一點,在這裡只關注Spring對<bean>元素的解析過程,其它的解析過程不再分析。DefaultBeanDefinitionDocumentReader.java

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        // 具體的解析委託給BeanDefinitionParserDelegate來完成  
        // BeanDefinitionHolder是BeanDefinition的封裝類, 
        // 封裝了BeanDefinition、Bean的名字和別名, 用它來完成向IoC容器註冊.
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
                bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
                try {
                        BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
                }
                catch (BeanDefinitionStoreException ex) {
                        getReaderContext().error("Failed to register bean definition with name '" +
                                bdHolder.getBeanName() + "'", ele, ex);
                }
                getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
        }
}

BeanDefinitionParserDelegate.java

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
        return parseBeanDefinitionElement(ele, null);
}
	
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
        // 解析Bean定義資原始檔中的<Bean>元素,主要處理<Bean>元素的id,name和aliase屬性
        String id = ele.getAttribute(ID_ATTRIBUTE);
        String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

        List<String> aliases = new ArrayList<String>();
        if (StringUtils.hasLength(nameAttr)) {
                String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                aliases.addAll(Arrays.asList(nameArr));
        }

        String beanName = id;
        // 如果<Bean>元素中沒有配置id屬性時, 將別名中的第一個值賦值給beanName
        if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
                beanName = aliases.remove(0);
                if (logger.isDebugEnabled()) {
                        logger.debug("No XML 'id' specified - using '" + beanName +
                                "' as bean name and " + aliases + " as aliases");
                }
        }

        if (containingBean == null) {
                checkNameUniqueness(beanName, aliases, ele);
        }

        // 對<bean>元素進行詳細解析
        AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
        if (beanDefinition != null) {
                if (!StringUtils.hasText(beanName)) {
                        try {
                                if (containingBean != null) {
                                        beanName = BeanDefinitionReaderUtils.generateBeanName(
                                        beanDefinition, this.readerContext.getRegistry(), true);
                                }
                                else {
                                        beanName = this.readerContext.generateBeanName(beanDefinition);
                                        //為解析的Bean使用別名註冊時, 為了向後相容(Spring1.2/2.0給別名新增類名字尾)
                                        String beanClassName = beanDefinition.getBeanClassName();
                                        if (beanClassName != null &&
                                                beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                                                !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                                                        aliases.add(beanClassName);
                                        }
                                }
                                if (logger.isDebugEnabled()) {
                                        logger.debug("Neither XML 'id' nor 'name' specified - " +
                                                "using generated bean name [" + beanName + "]");
                                }
                        }
                        catch (Exception ex) {
                                error(ex.getMessage(), ele);
                                return null;
                        }
                }
                String[] aliasesArray = StringUtils.toStringArray(aliases);
                return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
        }

        return null;
}
	
public AbstractBeanDefinition parseBeanDefinitionElement(
                Element ele, String beanName, BeanDefinition containingBean) {

        this.parseState.push(new BeanEntry(beanName));

        // 這裡只讀取<Bean>元素中配置的class名字, 然後載入到BeanDefinition中去    
        // 只是記錄配置的class名字, 並不例項化, 物件的例項化在依賴注入時完成  
        String className = null;
        if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
                className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
        }

        try {
                String parent = null;
                if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
                        parent = ele.getAttribute(PARENT_ATTRIBUTE);
                }
                // 根據<Bean>元素配置的class名稱和parent屬性值建立BeanDefinition, 為載入Bean定義資訊做準備  
                AbstractBeanDefinition bd = createBeanDefinition(className, parent);
                
                // 對當前<Bean>元素中配置的一些屬性進行解析, 如singleton、abstract等  
                parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
                bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
                
                // 對<Bean>元素的meta(元資料)、lookup-method、replaced-method等子元素進行解析  
                parseMetaElements(ele, bd);
                parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
                parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
                
                parseConstructorArgElements(ele, bd);    // 解析<Bean>元素的構造方法引數  
                parsePropertyElements(ele, bd);    // 解析<Bean>元素的<property>設定  
                parseQualifierElements(ele, bd);

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

                return bd;
        }
        catch (ClassNotFoundException ex) {
                error("Bean class [" + className + "] not found", ele, ex);
        }
        catch (NoClassDefFoundError err) {
                error("Class that bean class [" + className + "] depends on not found", ele, err);
        }
        catch (Throwable ex) {
                error("Unexpected failure during bean definition parsing", ele, ex);
        }
        finally {
                this.parseState.pop();
        }

        return null;
}

         <bean>元素解析已經完了,而<bean>元素屬性及其子元素的解析順序為:1,解析<bean>元素的屬性。2,解析<description>子元素。3,解析<meta>子元素。4,解析<lookup-method/>子元素。5,解析<replaced-method>子元素。6,解析<constructor-arg>子元素。7,解析<property>子元素。8,解析<qualifier>子元素。解析過程中像<meta>、<qualifier>等子元素都很少使用,而下面就直接解析最常用的子元素<property>子元素。

BeanDefinitionParserDelegate.java

public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
        // 遍歷<bean>所有的子元素
        NodeList nl = beanEle.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (isCandidateElement(node) && nodeNameEquals(node, PROPERTY_ELEMENT)) {
                        parsePropertyElement((Element) node, bd);    // 如果是<property>元素, 則對其進行解析
                }
        }
}

public void parsePropertyElement(Element ele, BeanDefinition bd) {
        String propertyName = ele.getAttribute(NAME_ATTRIBUTE);    // <property>元素name屬性
        if (!StringUtils.hasLength(propertyName)) {
                error("Tag 'property' must have a 'name' attribute", ele);
                return;
        }
        this.parseState.push(new PropertyEntry(propertyName));
        try {
                // 如果同一個Bean中已經有相同名字的<property>存在, 直接返回  
                // 也就是說, 如果一個Bean中定義了兩個名字一樣的<property>元素, 只有第一個起作用.
                if (bd.getPropertyValues().contains(propertyName)) {
                        error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
                        return;
                }
                // 解析<property>元素, 返回的物件對應<property>元素的解析結果, 最終封裝到PropertyValue中, 並設定到BeanDefinitionHolder中 
                Object val = parsePropertyValue(ele, bd, propertyName);
                PropertyValue pv = new PropertyValue(propertyName, val);
                parseMetaElements(ele, pv);
                pv.setSource(extractSource(ele));
                bd.getPropertyValues().addPropertyValue(pv);
        }
        finally {
                this.parseState.pop();
        }
}

public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
        String elementName = (propertyName != null) ?
                        "<property> element for property '" + propertyName + "'" :
                        "<constructor-arg> element";

        // 檢查<property>的子元素, 只能是ref, value, list等(description, meta除外)其中的一個. 
        NodeList nl = ele.getChildNodes();
        Element subElement = null;
        for (int i = 0; i < nl.getLength(); i++) {
                Node node = nl.item(i);
                if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
                        !nodeNameEquals(node, META_ELEMENT)) {
                        if (subElement != null) {
                                error(elementName + " must not contain more than one sub-element", ele);
                        }
                        else {
                                subElement = (Element) node;
                        }
                }
        }

        // 判斷property元素是否含有ref和value屬性, 不允許既有ref又有value屬性.  
        // 同時也不允許ref和value屬性其中一個與子元素共存.  
        boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
        boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
        if ((hasRefAttribute && hasValueAttribute) ||
                ((hasRefAttribute || hasValueAttribute) && subElement != null)) {
                error(elementName + 
                        " is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
        }

        // 如果屬性是ref屬性, 建立一個ref的資料物件RuntimeBeanReference, 封裝了ref資訊 
        if (hasRefAttribute) {
                String refName = ele.getAttribute(REF_ATTRIBUTE);
                if (!StringUtils.hasText(refName)) {
                        error(elementName + " contains empty 'ref' attribute", ele);
                }
                RuntimeBeanReference ref = new RuntimeBeanReference(refName);
                ref.setSource(extractSource(ele));
                return ref;
        }
        else if (hasValueAttribute) {    // 如果屬性是value屬性, 建立一個數據物件TypedStringValue, 封裝了value資訊 
                TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
                valueHolder.setSource(extractSource(ele));
                return valueHolder;
        }
        else if (subElement != null) {    // 如果當前<property>元素還有子元素 
                return parsePropertySubElement(subElement, bd);
        }
        else {    // propery元素既沒有ref或value屬性, 也沒有子元素, 解析出錯返回null  
                error(elementName + " must specify a ref or value", ele);
                return null;
        }
}

        恩,其實<property>元素還有子元素。

BeanDefinitionParserDelegate.java

public Object parsePropertySubElement(Element ele, BeanDefinition bd) {
        return parsePropertySubElement(ele, bd, null);
}

public Object parsePropertySubElement(Element ele, BeanDefinition bd, String defaultValueType) {
        if (!isDefaultNamespace(ele)) {    // 如果子元素沒有使用Spring預設名稱空間, 則使用使用者自定義的規則解析  
                return parseNestedCustomElement(ele, bd);
        }
        else if (nodeNameEquals(ele, BEAN_ELEMENT)) {    // 如果子元素是bean元素, 則使用解析<bean>元素的方法解析  
                BeanDefinitionHolder nestedBd = parseBeanDefinitionElement(ele, bd);
                if (nestedBd != null) {
                        nestedBd = decorateBeanDefinitionIfRequired(ele, nestedBd, bd);
                }
                return nestedBd;
        }
        else if (nodeNameEquals(ele, REF_ELEMENT)) {    // 如果子元素是ref, 有且只能有bean、local和parent 3個屬性中的一個
                String refName = ele.getAttribute(BEAN_REF_ATTRIBUTE);    // 引用普通任意的bean
                boolean toParent = false;
                if (!StringUtils.hasLength(refName)) {
                        refName = ele.getAttribute(LOCAL_REF_ATTRIBUTE);    // 引用同一個XML配置檔案中的bean 
                        if (!StringUtils.hasLength(refName)) {
                                refName = ele.getAttribute(PARENT_REF_ATTRIBUTE);    // 引用父容器中的bean 
                                toParent = true;
                                if (!StringUtils.hasLength(refName)) {
                                        error("'bean', 'local' or 'parent' is required for <ref> element", ele);
                                        return null;
                                }
                        }
                }
                // ref元素沒有bean、local和parent 3個屬性中的一個, 返回null. 
                if (!StringUtils.hasText(refName)) {
                        error("<ref> element contains empty target attribute", ele);
                        return null;
                }
                RuntimeBeanReference ref = new RuntimeBeanReference(refName, toParent);
                ref.setSource(extractSource(ele));
                return ref;
        }
        else if (nodeNameEquals(ele, IDREF_ELEMENT)) {    // 如果子元素是<idref>, 使用解析idref元素的方法解析
                return parseIdRefElement(ele);
        }
        else if (nodeNameEquals(ele, VALUE_ELEMENT)) {    // 子元素是<value>  
                return parseValueElement(ele, defaultValueType);
        }
        else if (nodeNameEquals(ele, NULL_ELEMENT)) {    // 子元素是<null>  
                TypedStringValue nullHolder = new TypedStringValue(null);
                nullHolder.setSource(extractSource(ele));
                return nullHolder;
        }
        else if (nodeNameEquals(ele, ARRAY_ELEMENT)) {    // 子元素是<array> 
                return parseArrayElement(ele, bd);
        }
        else if (nodeNameEquals(ele, LIST_ELEMENT)) {    // 子元素是<list> 
                return parseListElement(ele, bd);
        }
        else if (nodeNameEquals(ele, SET_ELEMENT)) {    // 子元素是<set>  
                return parseSetElement(ele, bd);
        }
        else if (nodeNameEquals(ele, MAP_ELEMENT)) {    // 子元素是<map>  
                return parseMapElement(ele, bd);
        }
        else if (nodeNameEquals(ele, PROPS_ELEMENT)) {    // 子元素是<props>  
                return parsePropsElement(ele);
        }
        else {    // 以上都不是, 說明配置錯誤, 返回null.  
                error("Unknown property sub-element: [" + ele.getNodeName() + "]", ele);
                return null;
        }
}

        我們以<list>子元素進行分析

BeanDefinitionParserDelegate.java

public List<Object> parseListElement(Element collectionEle, BeanDefinition bd) {
        // 獲取<list>元素中的value-type屬性, 即集合元素的資料型別
        String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
        NodeList nl = collectionEle.getChildNodes();    // <list>元素所有子節點
        ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
        target.setSource(extractSource(collectionEle));
        target.setElementTypeName(defaultElementType);
        target.setMergeEnabled(parseMergeAttribute(collectionEle));
        parseCollectionElements(nl, target, bd, defaultElementType);    // 具體解析List元素中的值
        return target;
}

protected void parseCollectionElements(
        NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {
        // 遍歷集合所有子節點
        for (int i = 0; i < elementNodes.getLength(); i++) {
                Node node = elementNodes.item(i);
                if (node instanceof Element && !nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
                        // 如果子節點是Element且不是<description>節點, 則新增進ManagedList.  
                        // 同時觸發對下層子元素的解析, 遞迴呼叫.
                        target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
                }
        }
}

        不好意思,沒子類了,BeanDefinition就被載入到IoC容器後,就可以在容器中建立了資料映射了。剩下的就是BeanDefinition註冊了。

BeanDefinition的註冊

           BeanDefinition資訊已經在IoC容器內部建立起了自己的資料結構,但這些資料還不能供IoC容器直接使用,需要在IoC容器中對這些BeanDefinition資料進行註冊。不同的解析元素解析方式都不同但最後的註冊的方式是一樣的,我們還是以上面提到的<bean>元素為例。

BeanDefinitionReaderUtils.java

public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
                    throws BeanDefinitionStoreException {

        String beanName = definitionHolder.getBeanName();
        // 向IoC容器註冊BeanDefinition
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

        // 如果解析的BeanDefinition有別名, 向容器為其註冊別名.  
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
                for (String alias : aliases) {
                        registry.registerAlias(beanName, alias);
                }
        }
}

         上面的registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition())是呼叫的註冊位置,而BeanDefinitionRegistry僅僅是一個介面,而真正實現它的卻是最原本的DefaultListableBeanFactory.registerBeanDefinition方法,值得一提的是DefaultListableBeanFactory.registerBeanDefinition方法在最新的Spring 4.0中穩定性方面做了很大改善。

DefaultListableBeanFactory.java

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException {

        Assert.hasText(beanName, "Bean name must not be empty");
        Assert.notNull(beanDefinition, "BeanDefinition must not be null");

        // 對解析得到的BeanDefinition校驗
        if (beanDefinition instanceof AbstractBeanDefinition) {
                try {
                        ((AbstractBeanDefinition) beanDefinition).validate();
                }
                catch (BeanDefinitionValidationException ex) {
                        throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                                "Validation of bean definition failed", ex);
                }
        }

        BeanDefinition oldBeanDefinition;

        oldBeanDefinition = this.