Spring5源码分析(011)——IoC篇之解析bean标签总览

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


  前面用 3 篇文章的篇幅阐述了 BeanDefinition 的整体的解析过程。在《Spring5源码分析(010)——IoC篇之加载BeanDefinition:解析和注册BeanDefinition》中提到过 Spring 中的标签包括默认标签和自定义标签两种,而这两种标签的用法以及解析方式存在着很大的不同:

  • 默认标签解析:DefaultBeanDefinitionDocumentReader.parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate)
  • 自定义标签解析:BeanDefinitionParserDelegate.parseCustomElement(Element ele)

  接下来将先从默认标签的解析过程开始进行分析。代码如下:

public static final String BEAN_ELEMENT = BeanDefinitionParserDelegate.BEAN_ELEMENT;
public static final String NESTED_BEANS_ELEMENT = "beans";
public static final String ALIAS_ELEMENT = "alias";
public static final String IMPORT_ELEMENT = "import";

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
    if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {    // import 标签
        importBeanDefinitionResource(ele);
    }
    else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {    // alias 标签
        processAliasRegistration(ele);
    }
    else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {    // bean 标签
        processBeanDefinition(ele, delegate);
    }
    else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {        // beans 标签,需要递归解析
        // recurse
        doRegisterBeanDefinitions(ele);
    }
}

  可以看到,这里一共涉及 import、alias、bean、beans 4 种标签:

  • import 标签主要是导入其他的 XML 配置文件,这需要定位加载资源文件然后又从头开始(递归)解析;
  • alias 标签主要是 bean 定义的别名;
  • beans 标签则直接递归解析;
  • 最复杂且最为重要的就是 bean 标签,集中了基本上所有的 bean 定义信息。

  接下来的博客会从 bean 标签的解析方法 DefaultBeanDefinitionDocumentReader.processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) 开始深入分析,如果能理解此标签的解析过程,其他标签的解析自然会迎刃而解,代码如下:

/**
 * 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));
    }
}

  从代码实现来看,整个过程可分为 4 个步骤:

  • 1、首先委托 BeanDefinitionParserDelegate.parseBeanDefinitionElement(Element ele) 方法进行元素解析,返回 BeanDefinitionHolder 实例对象 bdHolder ,即 解析 BeanDefinition 。
    • 如果解析成功,则 bdHolder 实例已经包含配置文件的各种属性,例如 class 、 name 、 id 、 alias 之类的属性。
      • BeanDefinitionHolder 内部有 beanDefinition 、 beanName 、 aliases 3 个属性,而其中的 beanDefinition 则是 bean 的具体定义。
    • 如果解析失败,则 bdHolder 实例为 null 。
  • 2、当返回的 bdHolder 不为空时,若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。
  • 3、解析完成后,委托 BeanDefinitionReaderUtils.registerBeanDefinition(...) 方法对解析后的 bdHolder 进行 BeanDefinition 的注册。
  • 4、发出响应事件,通知相关的监听器,完成 bean 加载。

时序图如下:

注:时序图来自插件 Sequence Diagram

  接下来的文章将分别对这4个步骤进行详细的分析:

(未完待续。。。)


参考

 

posted @ 2020-08-23 23:12  心明谭  阅读(180)  评论(0编辑  收藏  举报