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

本文详细解析了Spring中BeanDefinition从解析到注册到BeanFactory的全过程,涉及验证、注册前检查和注册BeanDefinition等关键步骤,帮助理解BeanFactory初始化的复杂性。
摘要由CSDN通过智能技术生成

向容器中注册 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 的整个流程,代码比较长,先大概捋一下流程,再分开详细讲解。在代码里我通过注释把代码大体分为两部分:

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-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 之后,就有了一个初级的容器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值