死磕Spring之IoC篇 - BeanDefinition 的解析阶段(XML 文件)

该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读

Spring 版本:5.1.14.RELEASE

开始阅读这一系列文章之前,建议先查看《深入了解 Spring IoC(面试题)》这一篇文章

该系列其他文章请查看:《死磕 Spring 之 IoC 篇 - 文章导读》

BeanDefinition 的解析阶段(XML 文件)

上一篇文章《BeanDefinition 的加载阶段(XML 文件)》获取到 org.w3c.dom.Document 对象后,需要通过 DefaultBeanDefinitionDocumentReader 进行解析,解析出 XML 文件中定义的 BeanDefinition 并进行注册,先来回顾一下上一篇文章中的这段代码:

public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { // <1> 创建 BeanDefinitionDocumentReader 对象 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); // <2> 获取已注册的 BeanDefinition 数量 int countBefore = getRegistry().getBeanDefinitionCount(); // <3> 创建 XmlReaderContext 对象(读取 Resource 资源的上下文对象) // <4> 根据 Document、XmlReaderContext 解析出所有的 BeanDefinition 并注册 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); // <5> 计算新注册的 BeanDefinition 数量 return getRegistry().getBeanDefinitionCount() - countBefore; }

本文开始分析第 4 步,BeanDefinition 的解析阶段,其中 BeanDefinitionDocumentReader 只有 DefaultBeanDefinitionDocumentReader 一个默认实现类

BeanDefinitionDocumentReader 接口

org.springframework.beans.factory.xml.BeanDefinitionDocumentReader,解析 DOM document 中的 BeanDefinition 并注册,代码如下:

public interface BeanDefinitionDocumentReader { /** * Read bean definitions from the given DOM document and * register them with the registry in the given reader context. * @param doc the DOM document * @param readerContext the current context of the reader * (includes the target registry and the resource being parsed) * @throws BeanDefinitionStoreException in case of parsing errors */ void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) throws BeanDefinitionStoreException; } DefaultBeanDefinitionDocumentReader

org.springframework.beans.factory.xml.DefaultBeanDefinitionDocumentReader,Spring 默认的 BeanDefinitionDocumentReader 实现类,从 XML 文件中解析出 BeanDefinition 并注册

构造函数 public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader { /** bean */ 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 NAME_ATTRIBUTE = "name"; public static final String ALIAS_ATTRIBUTE = "alias"; public static final String IMPORT_ELEMENT = "import"; public static final String RESOURCE_ATTRIBUTE = "resource"; public static final String PROFILE_ATTRIBUTE = "profile"; @Nullable private XmlReaderContext readerContext; /** * XML 文件的 BeanDefinition 解析器 */ @Nullable private BeanDefinitionParserDelegate delegate; }

上面定义了 XML 文件中常用的标签

1. registerBeanDefinitions 方法

registerBeanDefinitions(Document doc, XmlReaderContext readerContext) 方法,根据 Document、XmlReaderContext 解析出所有的 BeanDefinition 并注册,方法如下:

@Override public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; // 获得 XML Document Root Element // 执行注册 BeanDefinition doRegisterBeanDefinitions(doc.getDocumentElement()); } /** * Register each bean definition within the given root {@code <beans/>} element. */ @SuppressWarnings("deprecation") // for Environment.acceptsProfiles(String...) protected void doRegisterBeanDefinitions(Element root) { // 记录老的 BeanDefinitionParserDelegate 对象,避免再次调用当前方法时解析出现问题(默认值可能不同) BeanDefinitionParserDelegate parent = this.delegate; // <1> 创建 BeanDefinitionParserDelegate 对象 `delegate`,并初始化默认值 this.delegate = createDelegate(getReaderContext(), root, parent); // <2> 检查 <beans /> 根标签的命名空间是否为空,或者是 if (this.delegate.isDefaultNamespace(root)) { // <2.1> 获取 `profile` 属性 String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { // <2.2> 使用分隔符切分,可能有多个 `profile` 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. // <2.3> 根据 Spring Environment 进行校验,如果所有 `profile` 都无效,则不进行注册 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; } } } // <3> 解析前处理 preProcessXml(root); // <4> 解析出 XML Document 中的 BeanDefinition 并注册 parseBeanDefinitions(root, this.delegate); // <5> 解析后处理 postProcessXml(root); // 设置 delegate 回老的 BeanDefinitionParserDelegate 对象 this.delegate = parent; }

首先获取 XML Document 的最顶层的标签,也就是 <beans />,然后对其子标签进行解析,这里的过程大致如下:

创建 BeanDefinitionParserDelegate 对象 delegate,并初始化默认值

检查 <beans /> 根标签是否是默认命名空间(xmlns 属性,为空或者是 ),是的话进行校验

获取 profile 属性,使用分隔符切分

根据 Spring Environment 进行校验,如果所有 profile 都无效,则不进行注册

解析前处理,空方法,暂时忽略

解析出 XML Document 中的 BeanDefinition 并注册,调用 parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) 方法

解析后处理,空方法,暂时忽略

2. parseBeanDefinitions 方法

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygpww.html