歡迎光臨
每天分享高質量文章

【死磕 Spring】—– IOC 之 註冊 BeanDefinition

點選上方“Java技術驛站”,選擇“置頂公眾號”。

有內涵、有價值的文章第一時間送達!

精品專欄

 

獲取 Document 物件後,會根據該物件和 Resource 資源物件呼叫 registerBeanDefinitions() 方法,開始註冊 BeanDefinitions 之旅。如下:

  1.    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {

  2.        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();

  3.        int countBefore = getRegistry().getBeanDefinitionCount();

  4.        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));

  5.        return getRegistry().getBeanDefinitionCount() - countBefore;

  6.    }

首先呼叫 createBeanDefinitionDocumentReader() 方法實體化 BeanDefinitionDocumentReader 物件,然後獲取統計前 BeanDefinition 的個數,最後呼叫 registerBeanDefinitions() 註冊 BeanDefinition。

實體化 BeanDefinitionDocumentReader 物件方法如下:

  1.    protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {

  2.        return BeanDefinitionDocumentReader.class.cast(BeanUtils.instantiateClass(this.documentReaderClass));

  3.    }

註冊 BeanDefinition 的方法 registerBeanDefinitions() 是在介面 BeanDefinitionDocumentReader 中定義,如下:

  1.    void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)

  2.            throws BeanDefinitionStoreException;

從給定的 Document 物件中解析定義的 BeanDefinition 並將他們註冊到登錄檔中。方法接收兩個引數,待解析的 Document 物件,以及解析器的當前背景關係,包括標的登錄檔和被解析的資源。其中 readerContext 是根據 Resource 來建立的,如下:

  1.    public XmlReaderContext createReaderContext(Resource resource) {

  2.        return new XmlReaderContext(resource, this.problemReporter, this.eventListener,

  3.                this.sourceExtractor, this, getNamespaceHandlerResolver());

  4.    }

DefaultBeanDefinitionDocumentReader 對該方法提供了實現:

  1.    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {

  2.        this.readerContext = readerContext;

  3.        logger.debug("Loading bean definitions");

  4.        Element root = doc.getDocumentElement();

  5.        doRegisterBeanDefinitions(root);

  6.    }

呼叫 doRegisterBeanDefinitions() 開啟註冊 BeanDefinition 之旅。

  1.    protected void doRegisterBeanDefinitions(Element root) {

  2.        BeanDefinitionParserDelegate parent = this.delegate;

  3.        this.delegate = createDelegate(getReaderContext(), root, parent);

  4.        if (this.delegate.isDefaultNamespace(root)) {

  5.              // 處理 profile

  6.            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);

  7.            if (StringUtils.hasText(profileSpec)) {

  8.                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(

  9.                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);

  10.                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {

  11.                    if (logger.isInfoEnabled()) {

  12.                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +

  13.                                "] not matching: " + getReaderContext().getResource());

  14.                    }

  15.                    return;

  16.                }

  17.            }

  18.        }

  19.      // 解析前處理

  20.        preProcessXml(root);

  21.        // 解析

  22.        parseBeanDefinitions(root, this.delegate);

  23.        // 解析後處理

  24.        postProcessXml(root);

  25.        this.delegate = parent;

  26.    }

程式首先處理 profile屬性,profile主要用於我們切換環境,比如切換開發、測試、生產環境,非常方便。然後呼叫 parseBeanDefinitions() 進行解析動作,不過在該方法之前之後分別呼叫 preProcessXml()postProcessXml() 方法來進行前、後處理,目前這兩個方法都是空實現,交由子類來實現。

  1.    protected void preProcessXml(Element root) {

  2.    }

  3.    protected void postProcessXml(Element root) {

  4.    }

parseBeanDefinitions() 定義如下:

  1.    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {

  2.        if (delegate.isDefaultNamespace(root)) {

  3.            NodeList nl = root.getChildNodes();

  4.            for (int i = 0; i < nl.getLength(); i++) {

  5.                Node node = nl.item(i);

  6.                if (node instanceof Element) {

  7.                    Element ele = (Element) node;

  8.                    if (delegate.isDefaultNamespace(ele)) {

  9.                        parseDefaultElement(ele, delegate);

  10.                    }

  11.                    else {

  12.                        delegate.parseCustomElement(ele);

  13.                    }

  14.                }

  15.            }

  16.        }

  17.        else {

  18.            delegate.parseCustomElement(root);

  19.        }

  20.    }

最終解析動作落地在兩個方法處: parseDefaultElement(ele,delegate)delegate.parseCustomElement(root)。我們知道在 Spring 有兩種 Bean 宣告方式:

  • 配置檔案式宣告: id="studentService"class="org.springframework.core.StudentService"/>

  • 自定義註解方式: 

兩種方式的讀取和解析都存在較大的差異,所以採用不同的解析方法,如果根節點或者子節點採用預設名稱空間的話,則呼叫 parseDefaultElement() 進行解析,否則呼叫 delegate.parseCustomElement() 方法進行自定義解析。

至此, doLoadBeanDefinitions() 中做的三件事情已經全部分析完畢,下麵將對 Bean 的解析過程做詳細分析說明。

【死磕 Spring】----- IOC 之 獲取驗證模型

【死磕 Spring】----- IOC 之 載入 Bean

【死磕 Spring】----- IOC 之 Spring 統一資源載入策略

【死磕 Spring】----- IOC 之深入理解 Spring IoC

END

贊(0)

分享創造快樂

© 2024 知識星球   網站地圖