loadBeanDefinitions方法源碼跟蹤(一)

看這篇文章以前能夠先了解以前的跟蹤流程,https://www.jianshu.com/p/4934233f0eadjava

代碼過寬,能夠shift + 鼠標滾輪 左右滑動查看node

AbstractBeanDefinitionReader類中loadBeanDefinitions方法,該方法會對DOM文檔對象進行解析,生成BeanDefinition對象,這篇文章只講這個方法。web

/**
* Load bean definitions from the specified resources.
* @param resources the resource descriptors 資源處理器,也叫作資源描述符
* @return the number of bean definitions found 返回發現的bean definition數量
* 
* 從指定資源中加載bean definitions
*/
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
    Assert.notNull(resources, "Resource array must not be null");
    int counter = 0;
    
    //由於個人web.xml中配置contextConfigLocation無論classpath前綴仍是後半部分
    //都沒有使用 * 或者 ?等表達式,因此只有一個Resource--資源描述符,或者
    //稱做資源處理器
    for (Resource resource : resources) {
        
        //調用的子類XmlBeanDefinitionReader方法,進入查看
        counter += loadBeanDefinitions(resource);
    }
    return counter;
}

/**
* 此方法在AbstractBeanDefinitionReader的子類XmlBeanDefinitionReader中
*
* Load bean definitions from the specified XML file.
* 從指定的xml文件中加載bean definitions
*/
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    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());
    }
    
    //resourcesCurrentlyBeingLoaded是一個ThreadLocal,裏面存放Resource包裝類的set集合
    Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
    if (currentResources == null) {
        currentResources = new HashSet<EncodedResource>(4);
        this.resourcesCurrentlyBeingLoaded.set(currentResources);
    }
    
    //若是set中已有這個元素則返回false,進入該條件拋出異常
    if (!currentResources.add(encodedResource)) {
        throw new BeanDefinitionStoreException(
            
            //檢測到循環加載某個Resource,須要檢查導入的definitions
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
    }
    try {
        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();
        }
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
    }
    finally {
        
        //資源加載完畢,移除該Resource
        currentResources.remove(encodedResource);
        
        //若是集合中已經沒有了其餘Resource,移除集合
        if (currentResources.isEmpty()) {
            this.resourcesCurrentlyBeingLoaded.remove();
        }
    }
}

doLoadBeanDefinitions(零)

此方法在XmlBeanDefinitionReader類中,分兩部分跟蹤spring

/**
* Actually load bean definitions from the specified XML file.
* 
* 真正的從指定xml文件中加載bean definitions
*/
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    throws BeanDefinitionStoreException {
    try {
        
        //根據不一樣的xml約束(dtd,xsd等),將xml文件生成對應的文檔對象
        //1.這個方法裏面涉及xml的解析      
        Document doc = doLoadDocument(inputSource, resource);
        
        //2.主要就是看這個方法,bean definitions的註冊
        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);
    }
}

1.doLoadDocument

跟蹤標記1的方法app

此方法在XmlBeanDefinitionReader類中ide

//1.這個方法裏面涉及xml的解析      
Document doc = doLoadDocument(inputSource, resource);


/**
* Actually load the specified document using the configured DocumentLoader.
* @param inputSource the SAX InputSource to read from --從中讀取的SAX輸入源
* @param resource the resource descriptor for the XML file --xml文件的資源描述符
* @return the DOM Document DOM文檔對象
* 
* 使用配置好的DocumentLoader文檔加載器加載指定的文檔
*/
protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
    return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                                            getValidationModeForResource(resource), isNamespaceAware());
}

上面方法入參說明一下:post

參一

inputSource 由上一層方法傳遞過來。ui

參二

getEntityResolver() 方法返回 XmlBeanDefinitionReader 類的 entityResolver 屬性。
entityResolver 屬性在 loadBeanDefinitions(DefaultListableBeanFactory beanFactory) 方法中被賦值。this

beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

this拿的是根web應用上下文,查看ResourceEntityResolver的構造方法編碼

/**
* Create a ResourceEntityResolver for the specified ResourceLoader
* (usually, an ApplicationContext).
* @param resourceLoader the ResourceLoader (or ApplicationContext)
* to load XML entity includes with
* 
* 爲指定的ResourceLoade(一般是應用上下文)r建立一個ResourceEntityResolver
*/
public ResourceEntityResolver(ResourceLoader resourceLoader) {
    super(resourceLoader.getClassLoader());
    
    //此處解析器拿到了上下文的引用
    this.resourceLoader = resourceLoader;
}

調用了父類構造,再跟進一層

/**
* Create a new DelegatingEntityResolver that delegates to
* a default {@link BeansDtdResolver} and a default {@link PluggableSchemaResolver}.
* <p>Configures the {@link PluggableSchemaResolver} with the supplied
* {@link ClassLoader}.
* @param classLoader the ClassLoader to use for loading
* (can be {@code null}) to use the default ClassLoader)
*/
public DelegatingEntityResolver(ClassLoader classLoader) {
    
    //這兩個解析器和約束的類型有關,DTD
    this.dtdResolver = new BeansDtdResolver();
    
    //可插拔的Schema解析器,拿的上下文的類加載器
    this.schemaResolver = new PluggableSchemaResolver(classLoader);
}

參三

再看doLoadDocument方法的另一個入參 this.errorHandler,這個屬性隨着XmlBeanDefinitionReader類被初始化而初始化

private ErrorHandler errorHandler = new SimpleSaxErrorHandler(logger);

參四

而後是getValidationModeForResource(resource)入參。

/**
* Gets the validation mode for the specified {@link Resource}. If no explicit
* validation mode has been configured then the validation mode is
* {@link #detectValidationMode detected}.
* <p>Override this method if you would like full control over the validation
* mode, even when something other than {@link #VALIDATION_AUTO} was set.
* 
* 經過給定Resource給出驗證模式。若是沒有明確配置驗證模式,那麼調用detectValidationMode方法去檢測。
*/
protected int getValidationModeForResource(Resource resource) {
    
    //默認自動驗證,爲1
    int validationModeToUse = getValidationMode();
    
    //若是有給出具體驗證方式,則返回結果
    if (validationModeToUse != VALIDATION_AUTO) {
        return validationModeToUse;
    }
    
    //檢測驗證模式,進入這個方法
    int detectedMode = detectValidationMode(resource);
    if (detectedMode != VALIDATION_AUTO) {
        return detectedMode;
    }
    
    // Hmm, we didn't get a clear indication... Let's assume XSD,
    // since apparently no DTD declaration has been found up until
    // detection stopped (before finding the document's root tag).
    
    // 若是實在不能判斷驗證模式是那種就使用XSD方式,
    // 由於檢測完後仍是沒有發現DTD模式的聲明(在查找document的根標籤以前)。
    // 值爲3
    return VALIDATION_XSD;
}

進入下面這個方法,這個方法由XmlBeanDefinitionReader實現

int detectedMode = detectValidationMode(resource);

/**
* Detects which kind of validation to perform on the XML file identified
* by the supplied {@link Resource}. If the file has a {@code DOCTYPE}
* definition then DTD validation is used otherwise XSD validation is assumed.
* <p>Override this method if you would like to customize resolution
* of the {@link #VALIDATION_AUTO} mode.
* 
* 檢測執行xml文件時該用哪一種驗證方式,這個xml由Resource對象提供
* 若是這個文件有DOCTYPE聲明,那麼就用DTD驗證,不然就假定使用XSD。
* 若是你想要自定義自動驗證模式的解決方式,你能夠覆蓋這個方法
*/
protected int detectValidationMode(Resource resource) {
    
    //默認false
    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);
    }
    catch (IOException ex) {
        throw new BeanDefinitionStoreException("Unable to determine validation mode for [" +
                                               resource + "]: an error occurred whilst reading from the InputStream.", ex);
    }
}

XmlBeanDefinitionReader的validationModeDetector屬性有默認實現

private final XmlValidationModeDetector validationModeDetector = new XmlValidationModeDetector();

validationModeDetector調用detectValidationMode方法

/**
* Detect the validation mode for the XML document in the supplied {@link InputStream}.
* Note that the supplied {@link InputStream} is closed by this method before returning.
* 
* 在提供的InputStream中檢測XML文檔的驗證模式
* 注意,提供的InputStream在這個方法return以前會被關閉
*/
public int detectValidationMode(InputStream inputStream) throws IOException {
    
    // Peek into the file to look for DOCTYPE.
    // 查找文件的DOCTYPE
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
    try {
        boolean isDtdValidated = false;
        String content;
        while ((content = reader.readLine()) != null) {
            
            //讀一行字符串就幹掉字符串裏面的註釋,若是全是註釋全乾掉
            //主要爲了剝離註釋,由於非註釋內容要麼是DOCTYPE聲明要麼是文檔的根元素對象
            content = consumeCommentTokens(content);
            
            //剝離註釋後徹底沒內容就繼續循環
            if (this.inComment || !StringUtils.hasText(content)) {
                continue;
            }
            
            //有DOCTYPE聲明,就跳出去
            if (hasDoctype(content)) {
                isDtdValidated = true;
                break;
            }
            
            //註釋不能進去。開頭是"<",後面第一個字符是字母,就進入。
            //好比'<beans xmlns="http://www.springframework.org/schema/beans"'
            //進去後跳出循環
            if (hasOpeningTag(content)) {
                
                // End of meaningful data...
                break;
            }
        }
        
        //當遍歷到名稱空間了也就是"<beans xmlns=...>"尚未DOCTYPE聲明,
        //那麼就斷定他爲XSD驗證
        return (isDtdValidated ? VALIDATION_DTD : VALIDATION_XSD);
    }
    catch (CharConversionException ex) {
        
        // Choked on some character encoding...
        // Leave the decision up to the caller.
        return VALIDATION_AUTO;
    }
    finally {
        //關流
        reader.close();
    }
}

我這麼裏用的XSD驗證。也就是值爲3,傳遞給loadDocument的入參

參五

最後一個入參,isNamespaceAware()

/**
* Return whether or not the XML parser should be XML namespace aware.
*
* XML解析器是否支持XML名稱空間
*/
public boolean isNamespaceAware() {
    return this.namespaceAware;
}

默認false

看完五個參數後進入正主,loadDocument方法。

return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());

documentLoader屬性默認實現:

private DocumentLoader documentLoader = new DefaultDocumentLoader();

進入DefaultDocumentLoader類的loadDocument方法

/**
* Load the {@link Document} at the supplied {@link InputSource} using the standard JAXP-configured
* XML parser.
*
* 使用標準JAXP配置XML解析器加載InputSource的Document對象
*/
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
                             ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    
    //建立文檔構建器工廠對象,並初始化一些屬性
    //若是驗證模式爲XSD,那麼強制支持XML名稱空間,並加上schema屬性
    DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
    if (logger.isDebugEnabled()) {
        logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
    }
    
    //建立一個JAXP文檔構建器
    DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
    
    //按照XML文檔解析給定inputSource的內容,而後返回一個新的DOM對象
    return builder.parse(inputSource);
}

這樣就拿到了Document對象

回到XmlBeanDefinitionReader類的doLoadBeanDefinitions方法

2.registerBeanDefinitions

跟蹤標記2的方法

//2.主要就是看這個方法,bean definitions的註冊
return registerBeanDefinitions(doc, resource);


/**
* Register the bean definitions contained in the given DOM document.
* Called by {@code loadBeanDefinitions}.
* <p>Creates a new instance of the parser class and invokes
* {@code registerBeanDefinitions} on it.
* 
* 註冊包含在給定DOM文檔對象中的 bean definition
* 被loadBeanDefinitions方法所調用
* 解析class後建立一個新的實例,並調用registerBeanDefinitions方法
*/
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
    
    BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    
    //getRegistry()方法拿的是bean工廠對象,beanDefinition註冊在工廠中
    //這個方法就是返回已經被註冊在工廠中的beanDefinitions數量
    int countBefore = getRegistry().getBeanDefinitionCount();
    
    //進入這個方法查看
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    
    //返回上個方法真正註冊在工廠中的beanDefinition數量
    return getRegistry().getBeanDefinitionCount() - countBefore;
}


/**
* 這個方法在剛建立的DefaultBeanDefinitionDocumentReader中
*
* This implementation parses bean definitions according to the "spring-beans" XSD
* (or DTD, historically).
* <p>Opens a DOM Document; then initializes the default settings
* specified at the {@code <beans/>} level; then parses the contained bean definitions.
*
* 根據「spring-beans"的XSD(或者DTD)去解析bean definition
* 打開一個DOM文檔,而後初始化在<beans/>層級上指定的默認設置,而後解析包含在其中的bean definitions
*/
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    
    //入參時建立的XmlReaderContext對象
    this.readerContext = readerContext;
    logger.debug("Loading bean definitions");
    
    //拿到了xml文檔對象的根元素
    Element root = doc.getDocumentElement();
    
    //進入這個方法進行查看
    doRegisterBeanDefinitions(root);
}

查看DefaultBeanDefinitionDocumentReader類的doRegisterBeanDefinitions方法。

/**
* Register each bean definition within the given root {@code <beans/>} element.
*
* 在給定的根元素對象<beans/>中,註冊每個bean definition
*/
protected void doRegisterBeanDefinitions(Element root) {
    // Any nested <beans> elements will cause recursion in this method. In
    // order to propagate and preserve <beans> default-* attributes correctly,
    // keep track of the current (parent) delegate, which may be null. Create
    // the new (child) delegate with a reference to the parent for fallback purposes,
    // then ultimately reset this.delegate back to its original (parent) reference.
    // this behavior emulates a stack of delegates without actually necessitating one.
    
    // 任何被嵌套的<beans>元素都會致使此方法的遞歸。爲了正確的傳播和保存<beans>的默認屬性、
    // 保持當前(父)代理的跟蹤,它可能爲null
    // 爲了可以回退,新的(子)代理具備父的引用,最終會重置this.delegate回到它的初始(父)引用。
    // 這個行爲模擬了一堆代理,但實際上並不須要一個代理
    BeanDefinitionParserDelegate parent = this.delegate;
    
    //2.1建立一個新的代理,並初始化一些默認值
    this.delegate = createDelegate(getReaderContext(), root, parent);
    
    //默認名稱空間是"http://www.springframework.org/schema/beans"
    if (this.delegate.isDefaultNamespace(root)) {
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        
        //個人xml文件中沒有設置"profile",因此跳過
        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;
            }
        }
    }
    
    //xml預處理,子類沒有重寫裏面就是空實現
    preProcessXml(root);
    
    //2.2生成BeanDefinition,並註冊在工廠中
    parseBeanDefinitions(root, this.delegate);
    
    //xml後處理,子類沒有重寫裏面就是空實現
    postProcessXml(root);

    this.delegate = parent;
}

2.1 createDelegate

跟蹤標記2.1的方法

此方法在DefaultBeanDefinitionDocumentReader類中

入參 getReaderContext() 方法返回的是先前建立的 XmlReaderContext 對象

//2.1建立一個新的代理,並初始化一些默認值
this.delegate = createDelegate(getReaderContext(), root, parent);


protected BeanDefinitionParserDelegate createDelegate(
    XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
    
    //用來解析XML bean definition的有狀態代理類,用來被主解析器和其餘擴展使用
    BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext);
    
    //進入此方法
    delegate.initDefaults(root, parentDelegate);
    return delegate;
}

進入BeanDefinitionParserDelegate類的initDefaults方法

/**
* Initialize the default lazy-init, autowire, dependency check settings,
* init-method, destroy-method and merge settings. Support nested 'beans'
* element use cases by falling back to the given parent in case the
* defaults are not explicitly set locally.
* 
* 初始化默認值 default :·······等
* 經過使用 parent default,來解決嵌套的'beans'元素狀況,以防 default 在局部設定不明確
*/
public void initDefaults(Element root, BeanDefinitionParserDelegate parent) {
    
    //進入此方法
    populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);
    
    //默認沒作任何實現
    this.readerContext.fireDefaultsRegistered(this.defaults);
}

populateDefaults方法跟蹤

入參this.defaults有默認實現

private final DocumentDefaultsDefinition defaults = new DocumentDefaultsDefinition();

第二個入參:當第一次進入此方法時,parent爲null。

進入方法

/**
* Populate the given DocumentDefaultsDefinition instance with the default lazy-init,
* autowire, dependency check settings, init-method, destroy-method and merge settings.
* Support nested 'beans' element use cases by falling back to <literal>parentDefaults</literal>
* in case the defaults are not explicitly set locally.
*
* 用默認的值填充DocumentDefaultsDefinition實例
* 經過使用parentDefaults(父代理的default屬性),來解決嵌套的'beans'元素狀況,以防默認值在局部設定不明確
*/
protected void populateDefaults(DocumentDefaultsDefinition defaults, DocumentDefaultsDefinition parentDefaults, Element root) {
    
    //根元素上若是沒有設定值,則返回"default"字符串
    String lazyInit = root.getAttribute(DEFAULT_LAZY_INIT_ATTRIBUTE);
    
    //若是爲"default",先看parentDefaults有沒有,有用它的,沒有用"false"
    if (DEFAULT_VALUE.equals(lazyInit)) {
        
        // Potentially inherited from outer <beans> sections, otherwise falling back to false.
        
        // 可能從外部<beans>繼承,不然返回false
        lazyInit = (parentDefaults != null ? parentDefaults.getLazyInit() : FALSE_VALUE);
    }
    defaults.setLazyInit(lazyInit);

    String merge = root.getAttribute(DEFAULT_MERGE_ATTRIBUTE);
    if (DEFAULT_VALUE.equals(merge)) {
        
        // Potentially inherited from outer <beans> sections, otherwise falling back to false.
        merge = (parentDefaults != null ? parentDefaults.getMerge() : FALSE_VALUE);
    }
    defaults.setMerge(merge);

    String autowire = root.getAttribute(DEFAULT_AUTOWIRE_ATTRIBUTE);
    if (DEFAULT_VALUE.equals(autowire)) {
        
        // Potentially inherited from outer <beans> sections, otherwise falling back to 'no'.
        autowire = (parentDefaults != null ? parentDefaults.getAutowire() : AUTOWIRE_NO_VALUE);
    }
    defaults.setAutowire(autowire);

    // Don't fall back to parentDefaults for dependency-check as it's no longer supported in
    // <beans> as of 3.0. Therefore, no nested <beans> would ever need to fall back to it.
    
    // 依賴檢查不會採用parentDefaults,由於在3.0以後已再也不支持。所以,嵌套的<beans>不會使用parentDefaults
    defaults.setDependencyCheck(root.getAttribute(DEFAULT_DEPENDENCY_CHECK_ATTRIBUTE));

    if (root.hasAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE)) {
        defaults.setAutowireCandidates(root.getAttribute(DEFAULT_AUTOWIRE_CANDIDATES_ATTRIBUTE));
    }
    else if (parentDefaults != null) {
        defaults.setAutowireCandidates(parentDefaults.getAutowireCandidates());
    }

    if (root.hasAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE)) {
        defaults.setInitMethod(root.getAttribute(DEFAULT_INIT_METHOD_ATTRIBUTE));
    }
    else if (parentDefaults != null) {
        defaults.setInitMethod(parentDefaults.getInitMethod());
    }

    if (root.hasAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE)) {
        defaults.setDestroyMethod(root.getAttribute(DEFAULT_DESTROY_METHOD_ATTRIBUTE));
    }
    else if (parentDefaults != null) {
        defaults.setDestroyMethod(parentDefaults.getDestroyMethod());
    }
    
    //extractSource方法這裏沒有作任何實現,默認返回null
    defaults.setSource(this.readerContext.extractSource(root));
}

2.2 parseBeanDefinitions

跟蹤標記2.2的方法

在DefaultBeanDefinitionDocumentReader類中

//2.2生成BeanDefinition,並註冊在工廠中
parseBeanDefinitions(root, this.delegate);

/**
* Parse the elements at the root level in the document:
* "import", "alias", "bean".
*
* 解析在文檔中根層級的元素:"import", "alias", "bean".
*/
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
    
    //默認名稱空間是"http://www.springframework.org/schema/beans"
    //進入條件
    if (delegate.isDefaultNamespace(root)) {
        
        //獲取根元素下的子Node,注意,Node不必定是子標籤,多是回車,多是註釋
        NodeList nl = root.getChildNodes();
        for (int i = 0; i < nl.getLength(); i++) {
            Node node = nl.item(i);
            if (node instanceof Element) {
                
                //拿到了<beans>下的子標籤
                Element ele = (Element) node;
                if (delegate.isDefaultNamespace(ele)) {
                    
                    //3.若是該標籤屬於beans的名稱空間,則進入這個方法
                    //xmlns="http://www.springframework.org/schema/beans"
                    parseDefaultElement(ele, delegate);
                }
                else {
                    
                    //4.若是該標籤屬於其餘的名稱空間好比:context,aop等
                    //xmlns:aop="http://www.springframework.org/schema/aop"
                    //xmlns:context="http://www.springframework.org/schema/context"
                    delegate.parseCustomElement(ele);
                }
            }
        }
    }
    else {
        delegate.parseCustomElement(root);
    }
}

一個名稱空間對應一個處理器。

好比在spring的配置文件applicatinoContext.xml中加入了這行代碼:

<!-- 啓用註解 -->
<context:annotation-config />

能夠知道,標籤annotation-config屬於名稱空間context。

那麼context就對應着一個處理器。

解析的時候會先獲取context對應的處理器。處理器中又會初始化一些解析器出來,一個解析器就對應着一個標籤,像annotation-config就有對應本身的解析器。

不一樣的名稱空間、不一樣的標籤致使解析的方式很是的多,這裏只拿兩個常見的配置進行跟蹤。

由於字數超過了限制,因此分紅了三篇,點擊下篇繼續閱讀
https://www.jianshu.com/p/46e27afd7d96

總結

  • 解析 Resource 對象,生成DOM文檔對象
  • 2 註冊BeanDefinitions

——————————————————————————————————

  • 2
  • 建立新的 delegate 對象,delegate 中有 defaults 屬性,它是標籤元素屬性的一個集合。若是當前 Reader 對象中也有 delegate ,將其做爲 parent 。取 parent 的 defaults ,做爲未指定標籤元素屬性的狀況下,新 defaults 的默認值。
  • 根據名稱空間不一樣,分爲beans默認名稱空間的元素解析,和其餘自定義元素解析。通常來講,一個名稱空間對應一個處理器,處理器中又會初始化一些解析器出來。一個解析器就對應着一個標籤,能夠對不一樣的狀況進行解析。
相關文章
相關標籤/搜索