【超詳細的Spring源碼分析 —— 04 Spring對於Bean管理的核心組件源碼分析 - 解析Bean Definition】

這一篇的內容至關多,而且邏輯也會相對複雜,我儘量描述清楚 Spring 在解析 Bean Definition 時完成的工做。java

在這一篇中,分析的代碼入口是下面這個方法開始:node

doLoadBeanDefinitions(inputSource, encodedResource.getResource());
複製代碼

若是不清楚這個方法具體在哪一個位置去執行,能夠去看上一篇文章。spring

分析以前我先提醒下各位,必需要對以前咱們作的那些準備工做有一個清晰的概念,由於不少的組件就是在這裏被使用的。express

1、解析的入口

/** * 從指定xml配置文件中加載出Bean實例 * @param inputSource 以sax的形式進行讀取資源 * @param resource xml配置文件的資源描述 * @return 解析的bean個數 */
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
    try {
     	// 這裏會獲取到當前xml文件對應的Document對象
     	// 這個Document對象是一個xml的標準
     	// 簡單來講, 它就表明了咱們傳入的xml配置文件
     	// 它須要的兩個參數:
     	// 1. inputSource:內部封裝了配置文件的文件流以及定義的解析方式
     	// 2. resource:內部封裝了path路徑以及解析器(ClassLoader或Class實例)
     	// 別忘了inputSource就是經過resource來讀取的輸入流
     	// 其實咱們真正用到resource的, 也就是它內部的一個解析器
        Document doc = doLoadDocument(inputSource, resource);
        
        // 這個方法是關鍵中的關鍵!!!!!!!!!!!!!!!!!
     	// 進行文檔解析, 而後註冊Bean實例到工廠中
     	// 而後會返回一個解析並註冊的Bean的個數
        // 我會在下方詳細分析這個方法
        int count = registerBeanDefinitions(doc, resource);
        // 若是debug開啓, 記錄日誌
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        // 返回解析並註冊的bean個數
        return count;
    // 下面的邏輯就是捕獲並拋出相應的異常了
    // 咱們只須要注意, 異常的範圍應該從小到大, 而且應該儘量細粒度化
    // 從這一點來看, Spring也完成的至關好
    } 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);
    }
}
複製代碼

2、真真正正的解析 —— registerBeanDefinitions()

在分析以前,咱們先看一下這個方法須要提供的參數:設計模式

  • doc 文檔對象
  • resource 資源對象

它們之間的關係咱們須要先有一個清楚的認知:數組

  1. 咱們先經過資源對象中的 path 去讀取對應位置的 xml 配置文件的文件流,並封裝 sax 解析規則,生成了一個inputSource。
  2. 而後又經過 inputSource 與 resource,獲取到了xml配置文件對應的文檔對象

而後這個 registerBeanDefinitions() 方法,它又須要提供文檔對象以及 resource 實例。有沒有發現,自始至終,這個 resource 它都陰魂不散。bash

那麼 resource 做爲準備階段就構建好的組件,從如今開始,咱們最依賴它的,將會是它內部的解析器(ClassLoader/Class)。ide

/** * 註冊文檔中包含的Bean定義 * 返回解析並註冊到工廠的bean個數 */
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   // 建立文檔讀取器
   // 這個文檔讀取器就會負責bean的解析以及註冊工做
   // 咱們會在下方單獨把這個方法拎出來看看內部的邏輯
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   // 獲取到解析resource以前的, 容器中的bean的數量
   int countBefore = getRegistry().getBeanDefinitionCount();
   // 這個方法是整個註冊bean到容器的核心邏輯
   // 調用文檔讀取器去解析並註冊Bean
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   // 返回當前註冊的Bean個數
   // 這裏提一下:
   // 若是是在 xml中import了多個文檔, 那麼在第1次註冊動做之後, 容器中就有可能存在以前已經註冊的Bean實例
   // 所以這裏須要減去以前已經存在的BeanDifinition個數
   return getRegistry().getBeanDefinitionCount() - countBefore;
}
複製代碼

這個方法完成的核心邏輯有兩步:工具

  1. 建立 BeanDefinitionDocumentReader,也就是Bean定義文檔讀取器,它將負責從文檔對象中讀取出Bean Definition。
  2. 經過文檔讀取器去解析並註冊Bean Definition

下面咱們依次詳細分析這兩個步驟:ui

3、文檔讀取器的建立 —— createBeanDefinitionDocumentReader();

XmlBeanDefinitionReader.createBeanDefinitionDocumentReader()

/** * 建立一個文檔讀取器, 用於讀取文檔中的Bean定義 */
protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
    // 這裏調用了一個工具類來返回讀取器實例
    // 傳入的參數就是全局變量中的 documentReaderClass
    // 底層就是經過反射來獲取this.documentReaderClass這個引用指向的Class對象的實例
    return BeanUtils.instantiateClass(this.documentReaderClass);
}
複製代碼

工具類 BeanUtils.instantiateClass() 這個方法顧名思義,就是實例化指定的Class對象。所以咱們有必要對參數 this.documentReaderClass 進行一個簡單的瞭解。

documentReaderClass 是 XmlBeanDefinitionReader 這個類中聲明的全局變量,它默認指向一個DefaultBean定義讀取器的Class類。通常狀況下,咱們都會採用這個類的實例對象來做爲一個讀取器。

private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
			DefaultBeanDefinitionDocumentReader.class;
複製代碼

接着去粗略地看一下 DefaultBeanDefinitionDocumentReader 這個類

// 能夠發現, 該類包含了不少Spring配置文件中的標籤元素
// 實際上, 咱們在Spring配置文件中,可以使用的全部標籤以及屬性都在這個類中被定義了
public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {

    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 NAME_ATTRIBUTE = "name";
    
    public static final String ALIAS_ATTRIBUTE = "alias";
    
    public static final String IMPORT_ELEMENT = "import";
    
    public static final String RESOURCE_ATTRIBUTE = "resource";
    
    public static final String PROFILE_ATTRIBUTE = "profile";
    
    // 只是簡單瞭解, 所以忽略掉剩餘的代碼....
}
複製代碼

這邊我省略掉了 DefaultBeanDefinitionDocumentReader 這個類的大部分代碼,其實它還提供了諸多解析相關的API,感興趣能夠提早去本身看一下。

那麼咱們繼續分析下一個核心步驟 —— 經過文檔讀取器去解析並註冊Bean Definition。

4、經過文檔讀取器來解析並註冊Bean

咱們先看一下執行的代碼:

documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
複製代碼

能夠發現,這個方法須要傳遞文檔對象,以及一個讀取器上下文。

那麼首先咱們能夠簡單先看一下讀取器上下文相關的代碼:

/** * 建立一個XML讀取器上下文 */
public XmlReaderContext createReaderContext(Resource resource) {
	// 調用了一個 XML讀取器上下文的構造方法
	// 傳遞了諸多的參數, 包括資源、錯誤報告器、事件監聽器、命名空間解析器等
	return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
			this.sourceExtractor, this, getNamespaceHandlerResolver());
}
複製代碼

咱們再接着深刻到 XmlReaderContext 這個構造方法中:

public XmlReaderContext( Resource resource, ProblemReporter problemReporter, ReaderEventListener eventListener, SourceExtractor sourceExtractor, XmlBeanDefinitionReader reader, NamespaceHandlerResolver namespaceHandlerResolver) {
    super(resource, problemReporter, eventListener, sourceExtractor);
    this.reader = reader;
    this.namespaceHandlerResolver = namespaceHandlerResolver;
}
複製代碼

可以發現,讀取器上下文內部還封裝了XmlBeanDefinitionReader這個讀取器,所以咱們能夠簡單地將 XmlReaderContext 理解爲 「XmlBean定義讀取器的一個擴展」 。

ok,回到 registerBeanDefinitions() 相關的代碼:

DefaultBeanDefinitionDocumentReader.registerBeanDefinitions()

@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    // 將讀取器上下文對象賦值到文檔讀取器的全局變量中
    this.readerContext = readerContext;
    // 獲取到Document對象中的具體元素
    // 並經過這個元素開始解析並註冊文檔對象中的BeanDefinition
    doRegisterBeanDefinitions(doc.getDocumentElement());
}
複製代碼

DefaultBeanDefinitionDocumentReader.doRegisterBeanDefinitions()

/** * 經過給定的root元素, 註冊該元素下每個的Bean Definition * 這裏的root就是咱們在xml中定義的 <beans> 這個標籤 */
@SuppressWarnings("deprecation")
protected void doRegisterBeanDefinitions(Element root) {
    // 如下是Spring的原生註解翻譯:
    // 任何嵌套的<beans>標籤都會致使這個方法的遞歸調用
    // 就好比咱們經過在xml中 <import> 了另外一個xml, 那麼就會先遞歸處理另外一個xml文檔
    // 而且要記得, 在解析前的準備階段, Spring是已經處理完配置文件的循環依賴問題的
    
    // 這裏也很是關鍵
    // 在 DefaultBeanDefinitionDocumentReader 中有一個全局變量 delegate
    // 這個 delegate 纔是真正負責解析的組件
    // 這裏其實就採用了委託模式
    // DefaultBeanDefinitionDocumentReader 將具體的解析動做委託給了 delegate 完成
    // 可是這個 delegate 還未被初始化
    // 所以後續還會有一個初始化的流程
    // 這裏就先把delegate賦值給了一個BeanDefinitionParserDelegate類型的引用
    // 這個parent引用其實主要是用於delegate初始化時處理繼承關係
    BeanDefinitionParserDelegate parent = this.delegate;
    
    // 這裏就是具體建立 delegate 的邏輯了
    this.delegate = createDelegate(getReaderContext(), root, parent);
    
    // 若是 delegate 是採用默認命名空間
    if (this.delegate.isDefaultNamespace(root)) {
        // 獲取到 profile 屬性
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        // 處理 profile
        if (StringUtils.hasText(profileSpec)) {
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            // We cannot use Profiles.of(...) since profile expressions are not supported
            // in XML config. See SPR-12458 for details.
            if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
            	if (logger.isDebugEnabled()) {
                    logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource());
            	}
            	return;
            }
        }
    }
    // 前面的工做都至關於一個解析Bean以前的預處理, 後面纔是解析Bean的邏輯
    
    // 這裏採用了模板方法設計模式, 當咱們須要對DefaultBeanDefinitionDocumentReader進行一些擴展
    // 那麼只須要繼承, 並重寫preProcessXml、preProcessXml就可以實現這個方法的擴展
    // 執行的順序是由DefaultBeanDefinitionDocumentReader指定, 子類只須要負責擴展便可。
    
    // 默認空實現, 用於在解析文檔以前作一些預處理
    preProcessXml(root);
    
    // 具體解析流程, 委託設計模式, 將具體的解析交給 this.delegate 來完成
    parseBeanDefinitions(root, this.delegate);
    
    // 默認空實現, 用於在解析文檔以後作一些預處理
    preProcessXml(root);
    
    this.delegate = parent;
}
複製代碼

DefaultBeanDefinitionDocumentReader.parseBeanDefinitions();

這個方法涉及到 xml 解析相關的內容了。

簡單來講,就是遍歷獲取到根節點下,每個子標籤中的內容,而後進行解析。同時對那些不處於默認命名空間的子標籤進行一個客製化的解析。

/** * 解析root元素下的的 Bean Definition * 解析的標籤包含了: "import", "alias", "bean" * 就像我以前說的, 具體的解析是委託給了delegate */
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    
    if (delegate.isDefaultNamespace(root)) { // 解析root是不是默認命名空間當中的元素
    	NodeList nl = root.getChildNodes(); // 獲取到根節點root下的第一層子節點
    	for (int i = 0; i < nl.getLength(); i++) { // 遍歷獲取每個節點
            Node node = nl.item(i);
            // 判斷node是不是一個Element
            // 這裏主要是忽略掉註釋的解析
            if (node instanceof Element) {
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) { // 判斷給定節點是不是默認的命名空間
                    
                    //重點!!!
                    parseDefaultElement(ele, delegate); // 經過delegate去解析這個ele元素
                }
                else { // 若不是默認命名空間的元素, 則以客製化的方式去解析節點
                    delegate.parseCustomElement(ele);
                }
            }
    	}
    }
    else { // 若root不是默認命名空間的元素, 則以客製化的方式去解析節點
    	delegate.parseCustomElement(root);
    }
}
複製代碼

整個方法中,咱們須要重點關注的是 parseDefaultElement(ele, delegate); ,這個方法會去解析出 Bean Definition。

咱們接着看 parseDefaultElement() 的源碼:

DefaultBeanDefinitionDocumentReader.parseDefaultElement()

這個方法簡單來講,就是根據不一樣的子標籤,好比<bean><import>等,進行不一樣的解析處理。

咱們重點關注節點名稱爲 <bean> 的解析流程:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    // 節點名是import, 那麼就會處理資源引入
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
    	importBeanDefinitionResource(ele);
    }
    // 節點名是alias, 那麼就會處理別名的註冊
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
    	processAliasRegistration(ele);
    }
    // 節點名是bean, 就經過delegate處理
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
    	processBeanDefinition(ele, delegate);
    }
    // 若是節點名還包含了<beans>
    // 也就是 <beans> 中包含了 <beans>
    // 那麼就會進行一個 doRegisterBeanDefinitions() 方法的遞歸調用
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
    	// recurse
    	doRegisterBeanDefinitions(ele);
    }
}
複製代碼

DefaultBeanDefinitionDocumentReader.processBeanDefinition()

這個方法能夠說是本章的核心部分了,它包含了解析、裝飾、註冊 BeanDefinition 到工廠的入口。

/** * 經過解析器delegate去處理給定的bean element, 並解析出相應的bean Definition, 註冊到工廠中 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 經過delegate解析bean定義, 獲取到一個BeanDefinitionHolder實例
    // 這個實例, 其實就表明了xml文件中的一個完整的bean標籤對應的Bean Definition
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    
    // 若是bdHolder不爲空, 說明解析成功了, 以後就是註冊環節了
    // 註冊環節咱們留着在下一篇中詳細展開
    if (bdHolder != null) {
    	bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
    	try {
            // Register the final decorated instance.
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
    	}
    	catch (BeanDefinitionStoreException ex) {
            getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex);
    	}
    	// Send registration event.
    	getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}
複製代碼

咱們在本章不會涉及到 "註冊 BeanDefinition 到工廠的部分",重點關注解析 Bean 的流程。

至於裝飾這一塊:bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

感興趣的話能夠本身去了解一下,內容主要就是對客製化的標籤進行一些處理,將內容裝飾到 BeanDefinition 上。

5、具體的解析Bean Definition流程

這個流程咱們經過這段代碼進行展開:

BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
複製代碼

首先,深刻到 parseBeanDefinitionElement() 這個方法:

/** * Parses the supplied {@code <bean>} element. May return {@code null} * if there were errors during parse. Errors are reported to the * {@link org.springframework.beans.factory.parsing.ProblemReporter}. */
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    return parseBeanDefinitionElement(ele, null);
}
複製代碼

這裏又調用了一個重載的 parseBeanDefinitionElement() 方法

BeanDefinitionParserDelegate.parseBeanDefinitionElement()

這個方法會傳入兩個參數:

  • Element節點元素
  • 包含的bean Definition

它完成核心邏輯就是:對Element節點進行一系列的解析,獲取到一個包含了惟一標識,別名,以及屬性,聲明週期等等屬性的 BeanDefinitionHolder。

須要注意的是,這個 BeanDefinitionHolder 尚未包含被實例化的 Bean 實例。

@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
    String id = ele.getAttribute(ID_ATTRIBUTE); // 獲取到id屬性
    String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); // 獲取到name屬性, 通常咱們不會設置這個屬性
    
    List<String> aliases = new ArrayList<>();
    // 若是存在name屬性, 會對name進行相應的字符串處理, 並添加到aliases數組中
    if (StringUtils.hasLength(nameAttr)) {
    	String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
    	aliases.addAll(Arrays.asList(nameArr));
    }
    
    String beanName = id; // 將id做爲字符串beanName
    // 若是未指定id, 而且別名數組不爲空
    if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
    	// 就會從數組中移除第一個元素做爲beanName
    	beanName = aliases.remove(0);
    	if (logger.isTraceEnabled()) {
    		logger.trace("No XML 'id' specified - using '" + beanName +
    				"' as bean name and " + aliases + " as aliases");
    	}
    }
    
    // 檢查beanName的惟一性
    if (containingBean == null) {
        checkNameUniqueness(beanName, aliases, ele);
    }
    // 獲取到bean Definition
    // 這是個關鍵的方法, 一下子咱們會在下方詳細分析
    AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
    // 若是咱們解析出來的bean Definition不爲空
    if (beanDefinition != null) {
        // 但beanName這個表明bean惟一標識的字符串並未指定
    	if (!StringUtils.hasText(beanName)) {
            try { // 下面就是bean惟一標識生成的一些邏輯
            	if (containingBean != null) {
                    beanName = BeanDefinitionReaderUtils.generateBeanName(beanDefinition, this.readerContext.getRegistry(), true);
            	}
            	else { 
            	    // 經過讀取器上下文生成一個beanName
                    beanName = this.readerContext.generateBeanName(beanDefinition);
                    // 獲取到Bean的ClassName, 全限定類目
                    String beanClassName = beanDefinition.getBeanClassName();
                    // 別名的處理邏輯
                    if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                    	aliases.add(beanClassName);
                    }
            	}
            	if (logger.isTraceEnabled()) {
                    logger.trace("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);
    	
    	// 這裏會經過beanDefinition、beanName、別名數組來生成一個 BeanDefinitionHolder
    	// 這個beanName就是對應beanDefinition的一個映射
    	// 能夠認爲他倆是一個key-value關係
    	return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
    }
    return null;    
}
複製代碼

BeanDefinitionParserDelegate.parseBeanDefinitionElement()

這個方法的核心邏輯就是對 Element 進行一系列的處理,好比給返回的 beanDefinition 設置上 property 屬性、做用域 scope 等。

/** * 在不關聯惟一標識、別名、containingBean這些屬性的狀況下, 解析出bean definition, * 若解析過程當中出現異常, 會返回null */
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(Element ele, String beanName, @Nullable BeanDefinition containingBean) {

    this.parseState.push(new BeanEntry(beanName));
    
    //獲取全限定類目
    String className = null;
    if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
    	className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
    }
    
    // 獲取bean中定義的父類相關的屬性
    String parent = null;
    if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
    	parent = ele.getAttribute(PARENT_ATTRIBUTE);
    }
    try {
        // 根據全限定類名以及父類, 建立一個抽象的Bean定義
    	AbstractBeanDefinition bd = createBeanDefinition(className, parent);
        
        // 解析Bean定義的屬性, 其實就是一堆if語句塊
        // 判斷<bean> 標籤中是否存在指定的屬性, 若是存在, 那麼給db實例set上相應的屬性
        // 好比做用域scope、又好比初始化前的預處理init_method等等
    	parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
    	
    	// 設置描述信息
    	bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
        // 解析相應的元數據信息, 並set到db中
    	parseMetaElements(ele, bd);
    	
    	// 解析查詢的被複寫的子元素的信息
    	parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
    	// 解析可替換的方法的子元素的信息
    	parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
        
        // 解析構造方法參數
        // 好比咱們經過<construct-arg>實現bean屬性注入, 就是在這裏進行解析
    	parseConstructorArgElements(ele, bd);
    	
    	// 解析屬性參數
    	// 好比咱們經過<property>實現bean的屬性注入, 就在這裏解析
    	parsePropertyElements(ele, bd);
    	
    	// 解析Qualifier屬性參數
    	parseQualifierElements(ele, bd);
    
        // set上一些資源相關的信息
    	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;
}

複製代碼

接下來咱們深刻到這段代碼中:AbstractBeanDefinition bd = createBeanDefinition(className, parent);

/** * 根據傳入的全限定類名以及父類的全限定類名, 建立出一個抽象Bean定義 */
protected AbstractBeanDefinition createBeanDefinition(@Nullable String className, @Nullable String parentName) throws ClassNotFoundException {
    // 這邊會調用一個工具類提供的方法, 獲取到AbstractBeanDefinition實例
    // 在這個方法中額外添加了一個類加載器
    // 回憶一下咱們一開始建立的Resource實例, 它是建立好了一個解析器的(Class對象或者是ClassLoader)
    // 而後根據這個resource, 咱們又建立了XML讀取器上下文
    // 也就是說, 這個讀取器上下文內部的一個解析器, 就是最開始resource中準備好的那個解析器
    return BeanDefinitionReaderUtils.createBeanDefinition(parentName, className, this.readerContext.getBeanClassLoader());
}
複製代碼

BeanDefinitionReaderUtils.createBeanDefinition()

經過這個方法,就可以建立出一個抽象的 BeanDefinition,這個 BeanDefinition 會包含待實例化 Bean 的 Class 對象或全限定類名。

public static AbstractBeanDefinition createBeanDefinition(@Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {
    // 這個GenericBeanDefinition實例會封裝:待實例化的Bean的Class對象,或者是待實例化的Bean的全限定類名
    GenericBeanDefinition bd = new GenericBeanDefinition();
    
    // 首先set上父類的name
    bd.setParentName(parentName);
    if (className != null) { // 若是全限定類名不爲空
    	if (classLoader != null) { // 而且類加載器不爲空
    	    // 那麼就會經過ClassUtils.forName() 以反射的方式來獲取到一個Class類型的實例
    	    // 以後把Class對象給set到GenericBeanDefinition實例上
            bd.setBeanClass(ClassUtils.forName(className, classLoader));
    	}
    	else { // 若是類加載器爲空, 那麼只設置上全限定類名
    	    // 其實setBeanClass、setBeanClassName賦的全局變量都是通一個Object類型 —— beanClass
    	    // 這個 beanClass 既能夠接受一個字符串類型的參數(全限定類名)、又能夠接受一個Class類型的參數
            bd.setBeanClassName(className);
    	}
    }
    return bd;
}
複製代碼

6、總結

該篇文章主要涉及的是解析 BeanDefinition 的相關邏輯,由於邏輯層次比較複雜,這裏我採用圖片的形式進行一個全流程的總結:

在下一篇,將帶來註冊BeanDefinition到容器的具體分析。

相關文章
相關標籤/搜索