【死磕 Spring】----- IOC 之 註冊 BeanDefinition

原文出自:http://cmsblogs.comjava

獲取 Document 對象後,會根據該對象和 Resource 資源對象調用 registerBeanDefinitions() 方法,開始註冊 BeanDefinitions 之旅。以下:node

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

首先調用 createBeanDefinitionDocumentReader() 方法實例化 BeanDefinitionDocumentReader 對象,而後獲取統計前 BeanDefinition 的個數,最後調用 registerBeanDefinitions() 註冊 BeanDefinition。spring

實例化 BeanDefinitionDocumentReader 對象方法以下:post

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

註冊 BeanDefinition 的方法 registerBeanDefinitions() 是在接口 BeanDefinitionDocumentReader 中定義,以下:測試

void registerBeanDefinitions(Document doc, XmlReaderContext readerContext)
            throws BeanDefinitionStoreException;

從給定的 Document 對象中解析定義的 BeanDefinition 並將他們註冊到註冊表中。方法接收兩個參數,待解析的 Document 對象,以及解析器的當前上下文,包括目標註冊表和被解析的資源。其中 readerContext 是根據 Resource 來建立的,以下:this

public XmlReaderContext createReaderContext(Resource resource) {
        return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
                this.sourceExtractor, this, getNamespaceHandlerResolver());
    }

DefaultBeanDefinitionDocumentReader 對該方法提供了實現:spa

public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        logger.debug("Loading bean definitions");
        Element root = doc.getDocumentElement();
        doRegisterBeanDefinitions(root);
    }

調用 doRegisterBeanDefinitions() 開啓註冊 BeanDefinition 之旅。debug

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

        if (this.delegate.isDefaultNamespace(root)) {
              // 處理 profile
            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)) {
                    if (logger.isInfoEnabled()) {
                        logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }

      // 解析前處理
        preProcessXml(root);
        // 解析
        parseBeanDefinitions(root, this.delegate);
        // 解析後處理
        postProcessXml(root);

        this.delegate = parent;
    }

程序首先處理 profile屬性,profile主要用於咱們切換環境,好比切換開發、測試、生產環境,很是方便。而後調用 parseBeanDefinitions() 進行解析動做,不過在該方法以前以後分別調用 preProcessXml()postProcessXml() 方法來進行前、後處理,目前這兩個方法都是空實現,交由子類來實現。code

protected void preProcessXml(Element root) {
    }
    
    protected void postProcessXml(Element root) {
    }

parseBeanDefinitions() 定義以下:對象

protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        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)) {
                        parseDefaultElement(ele, delegate);
                    }
                    else {
                        delegate.parseCustomElement(ele);
                    }
                }
            }
        }
        else {
            delegate.parseCustomElement(root);
        }
    }

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

  • 配置文件式聲明:<bean id="studentService" class="org.springframework.core.StudentService"/>
  • 自定義註解方式:<tx:annotation-driven>

兩種方式的讀取和解析都存在較大的差別,因此採用不一樣的解析方法,若是根節點或者子節點採用默認命名空間的話,則調用 parseDefaultElement() 進行解析,不然調用 delegate.parseCustomElement() 方法進行自定義解析。

至此,doLoadBeanDefinitions() 中作的三件事情已經所有分析完畢,下面將對 Bean 的解析過程作詳細分析說明。

相關文章
相關標籤/搜索