• Spring源码分析(九)解析默认标签中的自定义标签元素


    摘要:本文结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码。若有描述错误之处,欢迎指正。

    到这里我们已经完成了分析默认标签的解析与提取过程,或许涉及的内容太多,我们已经忘了是从哪个函数开始了,我们再次回顾下默认标签解析函数的起始函数:

    /**
     * Process the given bean element, parsing the bean definition
     * and registering it with the registry.
     */
    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.
                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));
        }
    }

    我们已经用了大量的篇幅分析了BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele)这句代码,接下来,我们要进行delegate.decorateBeanDefinitionIfRequired(ele, bdHolder)代码的分析,首先大致了解下这句代码的作用,其实我们可以从语义上分析:如果需要的话就对beanDefinition进行装饰,那这句代码到底是什么功能呢?其实这句代码适用于这样的场景,如:

    <bean id = "test" class = "test.MyClass">
        <mybean:user username = "jason" />
    </bean>

     当Spring中的bean使用的是默认的标签配置,但是其中的子元素却使用了自定义的配置时,这句代码便会起作用了。可能有人会有疑问,之前讲过,对bean的解析分为两种类型,一种是默认类型的解析,另一种是自定义类型的解析,这不正是自定义类型的解析吗?为什么会在默认类型解析中单独添加一个方法处理呢?确实,这个问题很让人迷惑,但是,不知道聪明的读者是否有发现,这个自定义类型并不是以Bean的形式出现。我们之前讲过的两种类型的不同处理只是针对Bean的,这里我们看到,这个自定义类型其实是属性。好了,我们继续分析下这段代码的逻辑。

    public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder definitionHolder) {
        return decorateBeanDefinitionIfRequired(ele, definitionHolder, null);
    }

    这里将函数中第三个参数设置为空,那么第三个参数是做什么用的呢?什么情况下不为空呢?其实这第三个参数是父类bean,当对某个嵌套配置进行分析时,这里需要传递父类beanDefinition。分析源码得知这里传递的参数其实是为了使用父类的scope属性,以备子类若没有设置scope时默认使用父类的属性,这里分析的是顶层的配置,所以传递null。将第三个参数设置为空后进一步跟踪代码:

    public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
            Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) {
    
        BeanDefinitionHolder finalDefinition = definitionHolder;
    
        // Decorate based on custom attributes first.
        NamedNodeMap attributes = ele.getAttributes();
        // 遍历所有的属性,看看是否有适用于修饰的属性
        for (int i = 0; i < attributes.getLength(); i++) {
            Node node = attributes.item(i);
            finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
        }
    
        // Decorate based on custom nested elements.
        NodeList children = ele.getChildNodes();
        // 遍历所有的子节点,看看是否有适用于修饰的子元素
        for (int i = 0; i < children.getLength(); i++) {
            Node node = children.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                finalDefinition = decorateIfRequired(node, finalDefinition, containingBd);
            }
        }
        return finalDefinition;
    }

    上面的代码,我们看到函数分别对元素的所有属性以及子节点进行了decorateIfRequired函数的调用,我们继续跟踪代码:

    public BeanDefinitionHolder decorateIfRequired(
            Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {
    
        // 获取自定义标签的命名空间
        String namespaceUri = getNamespaceURI(node);
        // 对于非默认标签进行装饰
        if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
            // 根据命名空间找到对应的处理器
            NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
            if (handler != null) {
                // 进行装饰
                BeanDefinitionHolder decorated =
                        handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd));
                if (decorated != null) {
                    return decorated;
                }
            }
            else if (namespaceUri.startsWith("http://www.springframework.org/")) {
                error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", node);
            }
            else {
                // A custom namespace, not to be handled by Spring - maybe "xml:...".
                if (logger.isDebugEnabled()) {
                    logger.debug("No Spring NamespaceHandler found for XML schema namespace [" + namespaceUri + "]");
                }
            }
        }
        return originalDef;
    }

    程序走到这里,条理其实已经非常清楚了,首先获取属性或者元素的命名空间,以此来判断该元素或者属性是否适用于自定义标签的解析条件,找出自定义类型所对应的NamespaceHandler并进行进一步解析。在自定义标签解析的章节我们会重点讲解,这里暂时先略过。

    我们总结下decorateBeanDefinitionIfRequired方法的作用,在decorateBeanDefinitionIfRequired中我们可以看到对于程序默认的标签的处理其实是直接略过的,因为默认的标签到这里已经被处理完了,这里只对自定义的标签或者说对bean的自定义属性感兴趣。在方法中实现了寻找自定义标签并根据自定义标签寻找命名空间处理器,并进行进一步的解析。

  • 相关阅读:
    weblogic部署应用程序(转)
    Eclipse配置weblogic 10(转)
    设计模式之四:抽象工厂(披萨店生产披萨模拟流程)
    设计模式之七:适配器模式(火鸡转换成鸭子)
    ES6Array的扩展方法
    JavaScript闭包
    JavaScript函数进阶
    古龙语录
    JavaScript递归
    ES6let和const
  • 原文地址:https://www.cnblogs.com/warehouse/p/9380438.html
Copyright © 2020-2023  润新知