likes
comments
collection
share

Spring 源码阅读 09:加载 BeanDefinition 的过程(注册阶段)

作者站长头像
站长
· 阅读数 19

基于 Spring Framework v5.2.6.RELEASE

接上篇:Spring 源码阅读 08:加载 BeanDefinition 的过程(解析阶段)

前情提要

之前的两源码阅读中,分析了 Spring 加载 BeanDefinition 过程中准备阶段、 XML 加载阶段、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 是怎么到这儿的。

  • AbstractApplicationContextrefresh方法中,调用了obtainFreshBeanFactory方法,其中的refreshBeanFactory方法通过createBeanFactory方法创建了一个 DefaultListableBeanFactory,并赋值给了beanFactory成员变量。
  • 在解析 XML 文件之前,AbstractXmlApplicationContextloadBeanDefinitions方法中,创建 XmlBeanDefinitionReader 的时候,通过构造方法,将beanFactory赋值给了其registry成员变量,这一步通过其父类 AbstractBeanDefinitionReader 的构造方法完成。
  • 在 XmlBeanDefinitionReader 的registerBeanDefinitions方法中,通过createBeanDefinitionDocumentReader方法创建了 DefaultBeanDefinitionDocumentReader 类型的变量documentReader,并在调用documentReaderregisterBeanDefinitions方法时,通过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 的整个流程,代码比较长,先大概捋一下流程,再分开详细讲解。在代码里我通过注释把代码大体分为两部分:

  1. 对 BeanDefinition 进行注册前的检查
  2. 注册 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-methodreplaced-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;
}

这里就很简单了,将beanDefinitionbeanName分别添加到对应的集合中即可。

处理别名

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("...");
         }
      }
   }
}

方法中的逻辑如下:

  1. 如果beanName和别名相同,则将别名从aliasMap中移除
  2. 根据别名从aliasMap找到对应的beanName
    1. 如果可以找到,说明之前注册过了,直接返回,不执行后面的逻辑
    2. 如果找不到,就判断是不是允许别名覆盖,不允许就抛出异常
  3. 检查别名循环
  4. 将别名和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));
}

这里的逻辑也比较简单,举个例子就是,检查是不是存在一个beanNamea别名是b的 Bean,同时又存在一个beanNameb别名是a的Bean。如果有就抛出异常。

后续

至此,Spring 加载 BeanDefinition 的过程就全部分析完了,可以说这是 BeanFactory 初始化过程中最复杂的步骤。初始化完 BeanFactory 之后,就有了一个初级的容器。不过,在Spring 源码阅读 02:ApplicationContext 初始化 Spring 容器 中介绍过的容器初始化核心流程,也就是AbstractApplicationContext#refresh方法,到这里才进行了两步。

接下来会填一些之前文章中留下的坑,然后接着分析后续的流程。