注册解析的BeanDefinition

Posted wcj-java

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了注册解析的BeanDefinition相关的知识,希望对你有一定的参考价值。

                                              注册解析的BeanDefinition

      配置文件解析完了,对于得到的beanDefinition已经可以满足后续的使用要求了,唯一还剩下的工作就是注册了,也就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())代码的解析了。    

public static void registerBeanDefinition(
           BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
           throws BeanDefinitionStoreException {
 
        // 使用beanName做唯一标识注册.
        String beanName = definitionHolder.getBeanName();
        registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
 
        // 注册所有别名.
        String[] aliases = definitionHolder.getAliases();
        if (aliases != null) {
           for (String alias : aliases) {
               registry.registerAlias(beanName, alias);
           }
        }
    }

       从上面的代码可以看出,解析的beanDefinition都会被注册到BeanDefinitionRegistry类型的实例registry中,而对于beanDefinition的注册分成了两部分:通过beanName的注册以及通过别名的注册。

1通过beanName注册BeanDefinition

      对于beanDefinition的注册,你可能会觉得将beanDefinition直接放入map中就好了,使用beanName作为key。确实,Spring就是这么做的,只不过除此之外,它还做了别的事情。    

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");
        if (beanDefinition instanceof AbstractBeanDefinition) {
           try {
               // 注册前的最后一次校验,这里的校验不同于之前的xml文件校验,主要对于AbstractBeanDefinition属性中的methodOverrides校验
               // 校验methodOverrides是否与工厂方法并存或者methodOverrides对应的方法根本不存在。
               ((AbstractBeanDefinition) beanDefinition).validate();
           }
           catch (BeanDefinitionValidationException ex) {
               throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                       "Validation of bean definition failed", ex);
           }
        }
        BeanDefinition oldBeanDefinition;
        oldBeanDefinition = this.beanDefinitionMap.get(beanName);
        // 处理注册已经注册的beanName情况
        if (oldBeanDefinition != null) {
           // 如果对应的BeanName已经注册了且在配置中配置了bean不允许被覆盖,则抛出异常。
           if (!isAllowBeanDefinitionOverriding()) {
               throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                       "Cannot register bean definition [" + beanDefinition + "] for bean ‘" + beanName +
                       "‘: There is already [" + oldBeanDefinition + "] bound.");
           }
           else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
               // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
               if (this.logger.isWarnEnabled()) {
                   this.logger.warn("Overriding user-defined bean definition for bean ‘" + beanName +
                           "‘ with a framework-generated bean definition: replacing [" +
                           oldBeanDefinition + "] with [" + beanDefinition + "]");
               }
           }
           else if (!beanDefinition.equals(oldBeanDefinition)) {
               if (this.logger.isInfoEnabled()) {
                   this.logger.info("Overriding bean definition for bean ‘" + beanName +
                           "‘ with a different definition: replacing [" + oldBeanDefinition +
                           "] with [" + beanDefinition + "]");
               }
           }
           else {
               if (this.logger.isDebugEnabled()) {
                   this.logger.debug("Overriding bean definition for bean ‘" + beanName +
                           "‘ with an equivalent definition: replacing [" + oldBeanDefinition +
                           "] with [" + beanDefinition + "]");
               }
           }
           this.beanDefinitionMap.put(beanName, beanDefinition);
        }
        else {
           if (hasBeanCreationStarted()) {
               // Cannot modify startup-time collection elements anymore (for stable iteration)
               // 因为beanDefinitionMap是全局变量,这里定会存在并发访问的情况。
               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;
                   if (this.manualSingletonNames.contains(beanName)) {
                       Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
                       updatedSingletons.remove(beanName);
                       this.manualSingletonNames = updatedSingletons;
                   }
               }
           }
           else {
               // Still in startup registration phase
               // 注册beanDefinition
               this.beanDefinitionMap.put(beanName, beanDefinition);
               this.beanDefinitionNames.add(beanName);
               this.manualSingletonNames.remove(beanName);
           }
           this.frozenBeanDefinitionNames = null;
        }
        if (oldBeanDefinition != null || containsSingleton(beanName)) {
           // 重置所有beanName对应的缓存
           resetBeanDefinition(beanName);
        }
    }

 上面的代码中我们看到,在对于bean的注册处理方式上,主要进行了几个步骤。

(1)     对AbstractBeanDefinition的校验。在解析XML文件的时候我们提过校验,但是此校验非彼校验,之前的校验是针对XML格式的校验,而此时的校验是针对于AbstractBeanDefinition的methodOverrides属性的。

(2)     对beanName已经注册的情况的处理,如果设置了不允许bean的覆盖,则需要抛出异常,否则直接覆盖。

(3)     加入map缓存。

(4)     清楚解析之前留下的对应beanName的缓存。

2.通过别名注册BeanDefinition

      在理解了注册bean的原理后,理解注册别名的原理就容易多了。    

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) {
           // 如果beanName与alias相同的话不记录alias,并删除对应的alias。
           if (alias.equals(name)) {
               this.aliasMap.remove(alias);
           }
           else {
               String registeredName = this.aliasMap.get(alias);
               if (registeredName != null) {
                   if (registeredName.equals(name)) {
                       // An existing alias - no need to re-register
                       return;
                   }
                   // 如果alias不允许被覆盖则抛出异常。
                   if (!allowAliasOverriding()) {
                       throw new IllegalStateException("Cannot register alias ‘" + alias + "‘ for name ‘" +
                               name + "‘: It is already registered for name ‘" + registeredName + "‘.");
                   }
               }
               //当A->B存在时,若再次出现A->C->B时候则会抛出异常
               checkForAliasCircle(name, alias);
               this.aliasMap.put(alias, name);
           }
        }
    }

      由以上代码中可以得知注册alias的步骤如下:

(1)     alias与beanName相同情况处理。若alias与beanName名称相同则不需要处理并删除掉原有alias。

(2)     alias覆盖处理。若aliasName已经使用并已经指向了另一beanName则需要用户的设置进行处理。

(3)     alias循环检查。当A->B存在时,若再次出现A->C->B时候则会抛出异常。

(4)     注册alias。

以上是关于注册解析的BeanDefinition的主要内容,如果未能解决你的问题,请参考以下文章

注册解析的BeanDefinition

springIOC源码解析之BeanDefinition的注册

注册解析的BeanDefinition

[死磕 Spring 14/43] --- IOC 之注册解析的 BeanDefinition

SpringBoot -- 自动配置类AopAutoConfiguration解析注册BeanDefinition过程

SpringBoot -- 自动配置类AopAutoConfiguration解析注册BeanDefinition过程