美文网首页Spring IOC源码解读
spring源码日记05: DOM树默认名称空间解析

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

作者: BugPool | 来源:发表于2020-02-10 17:08 被阅读0次

    所有文章已迁移至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)
    • spring委托delegate进行DOM树的解析,其中需要注意的是<beans>存在嵌套,假如子<beans>中无default-*的相关信息,默认他需要继承外层<beans>的配置信息
    • preProcessXml,postProcessXml是2个没有final修饰的空方法(该设计思想非常重要,画重点!),典型的设计模式中的模板方法模式。该方法留给子类继承并完成override,让用户有机会能够干预spring的各个环节。spring大量的使用这种设计模式,包括我们后面要重点讲的后置处理器(BeanPostProcessor)
    // 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. 解析默认名称空间

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

    • import:<import resource = "config.xml"> 该标签递归调用加载资源,将重点分析
    • bean:spring最重要的标签,重点分析
    • alias:别名标签,该标签留给读者自行分析
    • beans:该标签递归调用doRegisterBeanDefinitions,这里将会用到之前讲到的<beans>标签嵌套,以及parent delegate继承的使用。留给读者自行分析
    // 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解析标签的准备工作才算做完,接下去标签的解析、注册才算刚刚开始

    相关文章

      网友评论

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

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