Spring 源码阅读 09:加载 BeanDefinition 的过程(注册阶段)
基于 Spring Framework v5.2.6.RELEASE
前情提要
之前的两源码阅读中,分析了 Spring 加载 BeanDefinition 过程中准备阶段、 XML 加载阶段、BeanDefinition的解析流程,可以参考:
- Spring 源码阅读 06:加载 BeanDefinition 的过程(准备阶段)
- Spring 源码阅读 07:加载 BeanDefinition 的过程(资源加载阶段)
- Spring 源码阅读 08:加载 BeanDefinition 的过程(解析阶段)
这一篇作为这个小系列的最终篇,通过阅读源代码,分析解析好的 BeanDefinition 如何被注册到 Spring 容器中。
向容器中注册 BeanDefinition
这部分从 DefaultBeanDefinitionDocumentReader 中的processBeanDefinition
方法说起:
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));
}
}
在解析完成之后,负责将 BeanDefinition 注册到容器中的是这样代码:
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
传入方法的参数,一个是之前解析道的 BeanDefinitionHolder,另一个其实就是我们最初创建的 BeanFactory(这里为什么是最初创建的 BeanFactory ?分析如下:)。
这里的
registry
就是我们起初创建的 BeanFacoty,之前涉及到的流程已经很多了,这里再捋一下起初创建的 BeanFacoty 是怎么到这儿的。
- 在
AbstractApplicationContext
的refresh
方法中,调用了obtainFreshBeanFactory
方法,其中的refreshBeanFactory
方法通过createBeanFactory
方法创建了一个DefaultListableBeanFactory
,并赋值给了beanFactory
成员变量。- 在解析 XML 文件之前,
AbstractXmlApplicationContext
的loadBeanDefinitions
方法中,创建 XmlBeanDefinitionReader 的时候,通过构造方法,将beanFactory
赋值给了其registry
成员变量,这一步通过其父类 AbstractBeanDefinitionReader 的构造方法完成。- 在 XmlBeanDefinitionReader 的
registerBeanDefinitions
方法中,通过createBeanDefinitionDocumentReader
方法创建了 DefaultBeanDefinitionDocumentReader 类型的变量documentReader
,并在调用documentReader
的registerBeanDefinitions
方法时,通过createReaderContext
创建了一个 XmlReaderContext 作为方法的参数传递给了documentReader
并赋值给了其readerContext
成员变量。- 上一步中,
createReaderContext
方法中,将当前对象也就是 XmlBeanDefinitionReader 对象作为参数传入并赋值给了reader
成员变量,而 DefaultBeanDefinitionDocumentReader 调用getReaderContext().getRegistry()
方法的时候,getReaderContext
获取到的是之前创建的 XmlReaderContext 对象,getRegistry
方法实际逻辑是this.reader.getRegistry()
,其中的this.reader
就是 XmlBeanDefinitionReader,因为 XmlBeanDefinitionReader 的beanFactory
成员变量就是起初创建的 BeanFactory。- 因此这里得到的就是最初创建的BeanFactory
我们跳进方法里看源码:
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
这里的关键步骤是registry.registerBeanDefinition
方法的调用。也就是 DefaultListableBeanFactory 中的registerBeanDefinition
方法:
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
// 第一部分,对 BeanDefinition 进行注册前的检查
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
// 第二部分,注册 BeanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("...");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("...");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("...");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
else if (isConfigurationFrozen()) {
clearByTypeCache();
}
}
以上两个方法就是注册 BeanDefination 的整个流程,代码比较长,先大概捋一下流程,再分开详细讲解。在代码里我通过注释把代码大体分为两部分:
- 对 BeanDefinition 进行注册前的检查
- 注册 BeanDefinition
下面分别深入分析。
注册前的检查
这部分涉及到的代码主要是validate
方法的调用:
public void validate() throws BeanDefinitionValidationException {
if (hasMethodOverrides() && getFactoryMethodName() != null) {
throw new BeanDefinitionValidationException("...");
}
if (hasBeanClass()) {
prepareMethodOverrides();
}
}
先看第一个if
语句的判断条件。
hasMethodOverrides()
方法获取到的是!this.methodOverrides.isEmpty()
。这里简单介绍一下methodOverrides
是什么。在将 XML 配置解析成 BeanDefinition 的时候,bean
标签的lookup-method
和replaced-method
会被分别解析成 LookupOverride 和 ReplaceOverride 对象,添加到 BeanDefinition 的methodOverrides
成员变量中。它们的作用是通过配置来覆盖 Bean 原有的方法实现。getFactoryMethodName()
获取到的是 XML 在bean
标签上配置的用来创建 Bean 的工厂方法。
这个if语句是为了确保,上述两者不能共存,否则就会报错。这是因为,如果给一个 Bean 配置了工厂方法,那么它会由工厂方法来创建,而不是 Spring 默认的方式创建,这种情况下无法进行方法的覆盖。因此,这里的判断是为了确保配置信息没有出现冲突。
再看第二个if
语句,判断条件hasBeanClass()
在我们分析的流程中肯定会返回true
,因此我们直接看语句块中的prepareMethodOverrides()
方法调用。
public void prepareMethodOverrides() throws BeanDefinitionValidationException {
// Check that lookup methods exist and determine their overloaded status.
if (hasMethodOverrides()) {
getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride);
}
}
protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException {
int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName());
if (count == 0) {
throw new BeanDefinitionValidationException("");
}
else if (count == 1) {
// Mark override as not overloaded, to avoid the overhead of arg type checking.
mo.setOverloaded(false);
}
}
这里针对配置了 MethodOverrides 的 BeanDefinition,从 Bean 对应的类中获取同名的方法,也就是要被覆盖的方法。
如果找不到,则报错,因为这算是一个配置错误。
如果需要被覆盖的方法同名方法在类中之存在 1 个,说明在类中没有它的重载方法,则将 MethodOverride 的overloaded
属性设置为false
。这一步是为了告诉 Spring 在覆盖方法的时候,不需要再去检查 Bean 中是否有其他的重载方法了,算是一个性能上的优化。
注册 BeanDefinition
接下来回到 DefaultListableBeanFactory 的registerBeanDefinition
方法的第二部分,开始进入注册 BeanDefinition 的流程。
首先,会从beanDefinitionMap
容器中根据当前 BeanDefinition 中配置的beanName
查找已经存在的 BeanDefinition:
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
因为我们这里分析的是初始化的流程,beanDefinitionMap
应该是空的,所以这里是获取不到已有的 BeanDefinition 的,并且目前是注册 BeanDefinition 的阶段,Bean 的创建也没有开始,所以,我们可以省略掉对应的逻辑,直接看注册的逻辑:
// 第二部分,注册 BeanDefinition
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
/* 前已经存在同名的 BeanDefinition 的逻辑*/
}
else {
if (hasBeanCreationStarted()) {
/* 已经开始 Bean 创建的逻辑 */
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
这里就很简单了,将beanDefinition
和beanName
分别添加到对应的集合中即可。
处理别名
DefaultListableBeanFactory 的registerBeanDefinition
方法执行完之后,又会回到 BeanDefinitionReaderUtils 的registerBeanDefinition
中的后半部分代码:
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
可以看出这里的流程是为了处理 BeanDefinition 的别名,我们跳到registerAlias方法去看一下,根据 DefaultListableBeanFactory 的继承关系,这个方法的实现可以在 SimpleAliasRegistry 中找到:
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
synchronized (this.aliasMap) {
if (alias.equals(name)) {
this.aliasMap.remove(alias);
if (logger.isDebugEnabled()) {
logger.debug("...");
}
}
else {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null) {
if (registeredName.equals(name)) {
// An existing alias - no need to re-register
return;
}
if (!allowAliasOverriding()) {
throw new IllegalStateException("...");
}
if (logger.isDebugEnabled()) {
logger.debug("...");
}
}
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
if (logger.isTraceEnabled()) {
logger.trace("...");
}
}
}
}
方法中的逻辑如下:
- 如果
beanName
和别名相同,则将别名从aliasMap
中移除 - 根据别名从
aliasMap
找到对应的beanName
- 如果可以找到,说明之前注册过了,直接返回,不执行后面的逻辑
- 如果找不到,就判断是不是允许别名覆盖,不允许就抛出异常
- 检查别名循环
- 将别名和
beanName
的对应关系添加到aliasMap
中(包括允许覆盖的情况)
整体逻辑比较简单,下面看一下检查别名循环的方法checkForAliasCircle
:
protected void checkForAliasCircle(String name, String alias) {
if (hasAlias(alias, name)) {
throw new IllegalStateException("Cannot register alias '" + alias +
"' for name '" + name + "': Circular reference - '" +
name + "' is a direct or indirect alias for '" + alias + "' already");
}
}
public boolean hasAlias(String name, String alias) {
String registeredName = this.aliasMap.get(alias);
return ObjectUtils.nullSafeEquals(registeredName, name) || (registeredName != null
&& hasAlias(name, registeredName));
}
这里的逻辑也比较简单,举个例子就是,检查是不是存在一个beanName
是a
别名是b
的 Bean,同时又存在一个beanName
是b
别名是a
的Bean。如果有就抛出异常。
后续
至此,Spring 加载 BeanDefinition 的过程就全部分析完了,可以说这是 BeanFactory 初始化过程中最复杂的步骤。初始化完 BeanFactory 之后,就有了一个初级的容器。不过,在Spring 源码阅读 02:ApplicationContext 初始化 Spring 容器 中介绍过的容器初始化核心流程,也就是AbstractApplicationContext#refresh
方法,到这里才进行了两步。
接下来会填一些之前文章中留下的坑,然后接着分析后续的流程。
转载自:https://juejin.cn/post/7133990436829397000