美文网首页
spring源码分析(二)配置文件的解析

spring源码分析(二)配置文件的解析

作者: 编程易行 | 来源:发表于2018-07-22 20:41 被阅读40次

    上一篇博客说明了下spring是如何找到资源文件的,classpath下的xml,最终会被解析为 ClassPathContextResource,下面进一步分析,有了这个资源文件之后spring是如何将其解析为BeanDefinition的

    入口XmlBeanDefinitionReader.loadBeanDefinitions

    最开始的入口,只是包了下Resource

    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        //EncodedResource只是存了Resource的编码
        return loadBeanDefinitions(new EncodedResource(resource));
    }
    

    1)保存当前正在加载的资源
    2)检测是否有重复加载资源的情况
    3)真正干活的地方doLoadBeanDefinitions

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        
        //... 省略日志
    
        //1、保存当前正在加载的资源
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            currentResources = new HashSet<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
    
        //2、检测是否有重复加载资源的情况
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
    
        InputStream inputStream = encodedResource.getResource().getInputStream();
        try {
            //document加载需要的的InputSource
            InputSource inputSource = new InputSource(inputStream);
            if (encodedResource.getEncoding() != null) {
                inputSource.setEncoding(encodedResource.getEncoding());
            }
            //3、真正干活的地方doLoadBeanDefinitions
            return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
        }
        finally {
            inputStream.close();
        }
    
        //... 省略 一系列异常处理
    }
    

    1、这里做的两件事,将资源文件解析成Document
    2、解析并注册BeanDefinitions

    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {
        
        Document doc = doLoadDocument(inputSource, resource);
        return registerBeanDefinitions(doc, resource);
        
    }
    
    protected Document doLoadDocument(InputSource inputSource, Resource resource) throws Exception {
        return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
                getValidationModeForResource(resource), isNamespaceAware());
    }
    

    document的解析

    XmlBeanDefinitionReader 将 Document的生成,委托给了DocumentLoader

    看下默认实现 DefaultDocumentLoader

    @Override
    public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
            ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
    
        //模板方法模式,子类继承DefaultDocumentLoader后可以替换DocumentBuilderFactory和DocumentBuilder的实现
        DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
        if (logger.isDebugEnabled()) {
            logger.debug("Using JAXP provider [" + factory.getClass().getName() + "]");
        }
        DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
        return builder.parse(inputSource);
    }
    

    这里使用了工厂方法模式


    解析xml 并 注册BeanDefinition

    有了document之后,就可以很方便的通过document的API来读取xml的元素。

    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        int countBefore = getRegistry().getBeanDefinitionCount();
        //beanDefininition的注册,委托给了BeanDefnitionDocumentReader
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }
    

    具体的注册调用过程,就不跟了,总之,最后会调用到org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader#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);
        }
    }
    
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }
    
    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        //import标签解析
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        //alias标签解析
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        //bean标签
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        //beans标签
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }
    

    到了这一步,spring开始配置文件解析,具体如何解析,以及如何将配置文件封装为BeanDefinitions并进行注册,下一篇博客会分析。

    相关文章

      网友评论

          本文标题:spring源码分析(二)配置文件的解析

          本文链接:https://www.haomeiwen.com/subject/qtulmftx.html