• 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 的任务就结束了。

  • 相关阅读:
    简易的设计模式——观察者模式
    简易的设计模式——桥梁模式
    static与并发
    如何编写优雅的代码:04. 设计模式(中)
    如何编写优雅的代码:03. 设计模式(上)
    如何编写优雅的代码:02. 设计原则
    如何编写优雅的代码:01. 概述
    .Net平台互操作技术:03. 技术验证
    .Net平台互操作技术:02. 技术介绍
    .Net平台互操作技术:01. 主要问题
  • 原文地址:https://www.cnblogs.com/jason0529/p/5239139.html
Copyright © 2020-2023  润新知