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 。
- 如果解析成功,则 bdHolder 实例已经包含配置文件的各种属性,例如 class 、 name 、 id 、 alias 之类的属性。
- 2、当返回的 bdHolder 不为空时,若存在默认标签的子节点下再有自定义属性,还需要再次对自定义标签进行解析。
- 3、解析完成后,委托 BeanDefinitionReaderUtils.registerBeanDefinition(...) 方法对解析后的 bdHolder 进行 BeanDefinition 的注册。
- 4、发出响应事件,通知相关的监听器,完成 bean 加载。
时序图如下:
注:时序图来自插件 Sequence Diagram
接下来的文章将分别对这4个步骤进行详细的分析:
- 1、解析 BeanDefinition
- 2、解析默认标签中的自定义标签
- 3、注册解析的 BeanDefinition
- 4、通知监听器解析及注册完成
(未完待续。。。)
参考
- spring 官方文档 5.2.3.RELEASE:https://docs.spring.io/spring-framework/docs/5.2.3.RELEASE/spring-framework-reference/core.html
- Spring源码深度解析(第2版),郝佳,P44-P72
- 相关注释可参考笔者 github 链接:https://github.com/wpbxin/spring-framework