Spring IOC源码解读

spring源码日记05: DOM树默认名称空间解析

2020-02-10  本文已影响0人  BugPool

所有文章已迁移至csdn,csdn个人主页https://blog.csdn.net/chaitoudaren

// XmlBeanDefinitionReader.java
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

        try {
            // 1.将文件流转化为DOM树
            Document doc = doLoadDocument(inputSource, resource);
            // 2.DOM树转化为BeanDefinition,并注册到对应map中
            int count = registerBeanDefinitions(doc, resource);
            if (logger.isDebugEnabled()) {
                logger.debug("Loaded " + count + " bean definitions from " + resource);
            }
            return count;
        }
        catch () {
            throw ...;
        }
    
    }

上节我们讲了inputStream -> DOM树的过程,也就是备注的第一点,现在我们从第二点,开始继续往下阅读。

// XmlBeanDefinitionReader.java
    public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
        this.readerContext = readerContext;
        // 真正开始解析DOM树,生产并注册BeanDefinitions
        doRegisterBeanDefinitions(doc.getDocumentElement());
    }

DOM树 -> BeanDefinition

  1. 创建解析DOM树的委托(delegate)
// DefaultBeanDefinitionDocumentReader.java
protected void doRegisterBeanDefinitions(Element root) {
        /**
         * delegate 指的是委托的意思,spring委托他进行DOM树的解析
         *
         * Xml文件中的<beans>标签是允许嵌套的,当出现嵌套时势必引起函数的递归调用,
         * 为了防止外层<beans>标签的default-*相关信息在子委托中丢失,确保内存<beans>能正确继承default-*相关信息
         * spring使用了堆栈的思想,先保存父委托,在将父委托传入以确保子委托的正确继承创建,函数最后即子委托结束后,再还原父委托
         */
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            /**
             * profile属性,用于在一个xml中实现多套配置
             * 例如切换开发和生产环境:
             * <beans profile = "dev"> ... </beans>
             * <beans profile = "pro"> ... </beans>
             */
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            if (StringUtils.hasText(profileSpec)) {
                String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
                        profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
                // We cannot use Profiles.of(...) since profile expressions are not supported
                // in XML config. See SPR-12458 for details.
                // 尝试在环境中指定profile可以接收的bean
                if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                                "] not matching: " + getReaderContext().getResource());
                    }
                    return;
                }
            }
        }

        // 预留给子类实现,在解析前进行自定义操作
        preProcessXml(root);
        // 解析及注册BeanDefinition,核心逻辑
        parseBeanDefinitions(root, this.delegate);
        // 预留给子类,解析后的操作
        postProcessXml(root);

        this.delegate = parent;
    }
  1. 解析DOM树

为了更好抓住主线,我们只分析默认名称空间的解析

// DefaultBeanDefinitionDocumentReader.java
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
        // 默认名称空间,本质是比较namespace是否等于"http://www.springframework.org/schema/beans"
        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);
        }
    }
  1. 解析默认名称空间

代码阅读到这里我们看到了几个熟悉的常量,这里不打算每个标签都进行讲解,还是抓住主线

// DefaultBeanDefinitionDocumentReader.java
    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标签,解析该标签实际是递归调用doRegisterBeanDefinitions,前面提到的parent
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

至此spring解析标签的准备工作才算做完,接下去标签的解析、注册才算刚刚开始

上一篇下一篇

猜你喜欢

热点阅读