Spring源碼入門——XmlBeanDefinitionReader解析


  接上篇【】 ,我們看到BeanDefinitionReader解決的是從資源文件(xml,propert)到BeanDefinition集合的過程。所以BeanDefinitionReader接口有兩個實現版本。

 

  BeanDefinitionReader的接口聲明,ResourceLoader是spring中解決Resource加載的操作。四個loadBeanDefinitions就是重載解決單個或者多個資源文件的處理問題。

  

  loadBeanDefinitions 是加載BeanDefinition接口的核心入口,AbstractBeanDefinitionReader中剩下了loadBeanDefinitions(Resource resource)這個方法給子類實現。

  開始加載資源文件,這一步會將Resource構造為一個EncodedResource,添加了編碼的相關控制信息。

1 //XmlBeanDefinitionReader.java
2     public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
3         return loadBeanDefinitions(new EncodedResource(resource));
4     }

  下一步需要去判斷當前配置文件是否被循環加載。

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());
        }
        //沒有明白為什么需要使用ThreadLoacal?
        //private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded = new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently being loaded");
        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!");
        }
        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 {
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }    

   真正執行加載操作的是doLoadBeanDefinitions方法。

protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        try {
       //獲取xml的校驗格式,dtd或者xsd的校驗模式
            int validationMode = getValidationModeForResource(resource);
       //解析xml為dom,用了sax解析的 Document doc
= this.documentLoader.loadDocument( inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
       //真正的主角來了
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); } }

  spring解析xml文件為org.w3c.dom.Document使用了SAX的實現方法,除了SAX,常用的xml解析方法還有dom,dom4j,jdom等方法。

  registerBeanDefinitions是正式注冊BeanDefinition的地方,這里又引入了一個新的接口BeanDefinitionDocumentReader(定義如何解析dom為BeanDefinition)。

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
     //創建一個解析器 BeanDefinitionDocumentReader documentReader
= createBeanDefinitionDocumentReader();
     //設置上下文環境 documentReader.setEnvironment(
this.getEnvironment()); int countBefore = getRegistry().getBeanDefinitionCount(); documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore; }

    解析的實現為reader.registerBeanDefinitions方法。

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

  緩存readerContext,目的不清楚。直接parse

    protected void doRegisterBeanDefinitions(Element root) {
        String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
        if (StringUtils.hasText(profileSpec)) {
            Assert.state(this.environment != null, "Environment must be set for evaluating profiles");
            String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                    profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
            if (!this.environment.acceptsProfiles(specifiedProfiles)) {
                return;
            }
        }

        // 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.
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createHelper(this.readerContext, root, parent);

        preProcessXml(root);
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

  preProcessXml和postProcessXml方法暫時留空,目的為了需要增加新的實現版本時候方便擴展。

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

  parseBeanDefinitions方法內將

    protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
     //判斷解析的BeanDefinition是spring定義的還是自定義擴展的
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); } }

  對於spring定義的BeanDefinition的解析以beans , bean , import , alias開頭的四種bean,其他標示開始認為是自定義bean。自定義bean的解析機制都要從NamespaceHandler接口出發。Spring-core和Spring-beans包內就提供了如下這些實現。

  

    下面是BeanDefinitionParserDelegate類中具體處理自定義bean的代碼,允許根據不同的nameSpaceUri查找當前應用下面定義的解析類

public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
        String namespaceUri = getNamespaceURI(ele);
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler == null) {
            error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
            return null;
        }
        return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
    }

  至此解析部分完成了。但是解析完成的BeanDefinition放到哪里了呢?看一個例子:

  

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
        BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
        if (bdHolder != null) {
            bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
            try {
                // Register the final decorated instance.
         //真正執行注冊BeanDefinition的類 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 的任務就結束了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM