Spring5源码分析(017)——IoC篇之解析bean标签:解析默认标签中的自定义标签

注:《Spring5源码分析》汇总可参考:Spring5源码分析(002)——博客汇总


  前面的长篇大论基本上算是讲完了 bean 标签中的默认属性和子元素解析,包括基本属性以及constructor-argpropertyqualifiermetalookup-methodreplaced-method 这6个子元素等,回顾下,这些分析所做的事情也即是 Spring5源码分析(011)——IoC篇之解析bean标签总览 中提到的第1步,即 BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); ,接下来需要处理的则是bean标签中的自定义标签解析,即代码中提到的【2、自定义标签解析(若默认标签下有自定义标签)】:

/**
 * Process the given bean element, parsing the bean definition
 * and registering it with the registry.
 * <p>处理给定的 bean 元素,解析 bean 定义并将其注册到 registry 中
 */
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    // 1、委托 BeanDefinitionParserDelegate 进行元素解析。解析成功,则返回的 BeanDefinitionHolder 实例
    // 已经包含配置文件中配置的各种属性,例如 class 、 name 、 id 、 alias 之类的属性。
    // 解析失败,则返回为 null
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if (bdHolder != null) {
        // 2、自定义标签解析(若默认标签下有自定义标签)
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
        try {
            // 3、注册 BeanDefinition
            // 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);
        }
        // 4、发出响应事件,通知相关的监听器,完成 bean 加载
        // Send registration event.
        getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }
}

   对于 bean 标签中的自定义标签解析,这里调用的是 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef),从方法名称我们可以大致猜到:如果需要的话就对 beanDefinition 进行装饰(即有自定义标签时才需要进行装饰,从笔者的实际使用来看,大部分场景下是用不上自定义标签的)。我们来看下一个简单的场景(参考[2]Spring源码深度解析(第2版),郝佳,P67-P69;详细可参考官方文档说明 [1]9.2. XML Schema Authoring https://docs.spring.io/spring-framework/docs/5.2.3.RELEASE/spring-framework-reference/core.html#xml-custom):

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

   当 Spring 中的bean标签使用的是默认的标签配置,而其子元素中却使用了自定义的配置时,这个调用才会起作用。之前在 Spring5源码分析(010)——IoC篇之加载BeanDefinition:解析和注册BeanDefinition 中讲过,在解析和注册 BeanDefinition 时,标签的解析分为2种类型,一种是默认标签的解析,另一种是自定义标签的解析,不过这两种都是针对bean的形式的。这里提到的正是自定义标签解析。那为什么还需要在默认类型解析中单独添加一个方法来处理呢?其实这里的自定义标签指的是bean标签中的属性/子元素,是bean中的一部分,并不能单独解析,而是需要存在于相应的bean。接下来具体分析下 decorateBeanDefinitionIfRequired 的逻辑。目录结构如下:

 

1、decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef)

  自定义标签的解析是通过 org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) 来进行处理的,而其内部则是委托 decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) 来进行解析:
/**
 * Decorate the given bean definition through a namespace handler, if applicable.
 * <p>如果适用的话,通过命名空间处理器装饰给定的beanDefinition
 * @param ele the current element
 * @param originalDef the current bean definition
 * @return the decorated bean definition
 */
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(Element ele, BeanDefinitionHolder originalDef) {
    return decorateBeanDefinitionIfRequired(ele, originalDef, null);
}

/**
 * Decorate the given bean definition through a namespace handler, if applicable.
 * @param ele the current element
 * @param originalDef the current bean definition
 * @param containingBd the containing bean definition (if any)
 * @return the decorated bean definition
 */
public BeanDefinitionHolder decorateBeanDefinitionIfRequired(
        Element ele, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

    BeanDefinitionHolder finalDefinition = originalDef;

    // Decorate based on custom attributes first.
    // 1、遍历所有属性,看看是否有需要装饰的自定义属性
    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.
    // 2、遍历所有的子节点/子元素,看看是否有需要装饰的自定义子元素
    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;
}

 注:实际处理函数中的第三个参数被设置为空,这个参数其实是父类bean,当对某个嵌套配置进行分析时,这里需要传递父类beanDefinition。进一步分析可以发现其实时为了使用父类的scope属性,以备子类若没有设置scope时默认使用父类的属性,这里分析的时顶层配置,所以传递了null。

  从上面的实现来看,自定义标签的解析其实就是遍历所有的属性(attributes)以及子元素(childNodes),然后再拿所有的属性和子元素去按需装饰,最终调用的都是 decorateIfRequired(Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd)
  

2、decorateIfRequired(Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd)

  decorateIfRequired 方法,根据自定义标签的节点Node,获取对应的命名空间处理器NamespaceHandler,然后进行装饰:
/**
 * Decorate the given bean definition through a namespace handler,
 * if applicable.
 * <p>如果适用的话,通过命名空间处理器装饰给定的beanDefinition
 * @param node the current child node
 * @param originalDef the current bean definition
 * @param containingBd the containing bean definition (if any)
 * @return the decorated bean definition
 */
public BeanDefinitionHolder decorateIfRequired(
        Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) {

    // 1、获取自定义标签的命名空间
    String namespaceUri = getNamespaceURI(node);
    // 2、对于非默认的自定义标签进行装饰
    if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) {
        // 3、根据命名空间找到对应的命名空间处理器NamespaceHandler
        NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
        if (handler != null) {
            // 4、使用自定义的命名空间处理器NamespaceHandler进行装饰
            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/schema/")) {
            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然后再做进一步的解析。详细的自定义配置解析后续文章会进一步分析,也可自行参考官网的详细说明 [1]9.2. XML Schema Authoring https://docs.spring.io/spring-framework/docs/5.2.3.RELEASE/spring-framework-reference/core.html#xml-custom

  至此,bean标签的解析分析已全部完成。
 

3、参考

posted @ 2020-11-23 22:42  心明谭  阅读(149)  评论(0编辑  收藏  举报