不要设置allow-bean-definition-overriding=true

本文探讨了Spring框架中bean定义覆盖的问题,解释了`spring.main.allow-bean-definition-overriding`属性的作用。默认情况下,Spring允许bean定义覆盖,但在Spring Boot中,默认设置为false。如果出现bean定义冲突,不应简单地开启覆盖,而应查找并解决重复的bean定义。这样做可以避免潜在的代码冲突和不可预见的行为。理解这一机制对于维护干净、稳定的Spring应用至关重要。

不要设置allow-bean-definition-overriding=true

Description:

The bean 'FeignClientSpecification' could not be registered. A bean with that name has already been defined and overriding is disabled.

Action:

Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true

你是不是也会经常遇到上面的问题,这时候你就会去百度,百度会很友好的回复你spring已经给你的提示,设置 spring.main.allow-bean-definition-overriding=true,设置了真的就没问题了吗?是的,没问题了,好了,不管了上线。但是这里的坑,你知道吗?

allow-bean-definition-overriding 到底是个啥?

DefaultListableBeanFactory.class

/**
 * Set whether it should be allowed to override bean definitions by registering
 * a different definition with the same name, automatically replacing the former.
 * If not, an exception will be thrown. This also applies to overriding aliases.
 * <p>Default is "true".
 * @see #registerBeanDefinition
 */
public void setAllowBeanDefinitionOverriding(boolean allowBeanDefinitionOverriding) {
   this.allowBeanDefinitionOverriding = allowBeanDefinitionOverriding;
}

我们先看下spring源码对于该字段的描述,发现默认就是true,哎,那为啥我们还要设置呢?那是因为springboot封装的时候默认成了false,源码如下:

SpringApplication.class

private boolean allowBeanDefinitionOverriding;
... ...
private void prepareContext(ConfigurableApplicationContext context,
      ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
      ApplicationArguments applicationArguments, Banner printedBanner) {
   context.setEnvironment(environment);
   postProcessApplicationContext(context);
   applyInitializers(context);
   listeners.contextPrepared(context);
   if (this.logStartupInfo) {
      logStartupInfo(context.getParent() == null);
      logStartupProfileInfo(context);
   }
   // Add boot specific singleton beans
   ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
   beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
   if (printedBanner != null) {
      beanFactory.registerSingleton("springBootBanner", printedBanner);
   }
   if (beanFactory instanceof DefaultListableBeanFactory) {
      ((DefaultListableBeanFactory) beanFactory)
            .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   // Load the sources
   Set<Object> sources = getAllSources();
   Assert.notEmpty(sources, "Sources must not be empty");
   load(context, sources.toArray(new Object[0]));
   listeners.contextLoaded(context);
}

那设置为true,spring会进行什么样的操作呢,我们继续看对应源码

DefaultListableBeanFactory.class

@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");

   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
               "Validation of bean definition failed", ex);
      }
   }

   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("Overriding user-defined bean definition for bean '" + beanName +
                  "' with a framework-generated bean definition: replacing [" +
                  existingDefinition + "] with [" + beanDefinition + "]");
         }
      }
      else if (!beanDefinition.equals(existingDefinition)) {
         if (logger.isDebugEnabled()) {
            logger.debug("Overriding bean definition for bean '" + beanName +
                  "' with a different definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      else {
         if (logger.isTraceEnabled()) {
            logger.trace("Overriding bean definition for bean '" + beanName +
                  "' with an equivalent definition: replacing [" + existingDefinition +
                  "] with [" + beanDefinition + "]");
         }
      }
      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();
   }
}

看源码可以看到,如果设置为true,再方法最后会 this.beanDefinitionMap.put(beanName, beanDefinition) 进行覆盖,这样就会导致bean被覆盖掉了,这是隐藏的大坑啊,如果同一个项目两个人默契的写了同一个bean,这时候有一个可怜虫就要被覆盖掉了,可能还不知道什么原因,这可太惨了。如果最后发现是你干的,你猜他会不会拿起40米的大刀。。。所以,遇到文章开始的问题,找对应重复bean根据实际使用删除或者修改,不要设置allow-bean-definition-overriding=true,切记切记~

### 启用 `allow-bean-definition-overriding` 的配置方式 在 Spring Boot 应用中,若多个配置类定义了相同名称的 Bean,且未启用 Bean 定义覆盖机制,启动时会抛出 `BeanDefinitionOverrideException`,提示某个 Bean 已被定义且覆盖机制被禁用。为解决此类问题,可以通过配置 `spring.main.allow-bean-definition-overriding=true` 来启用 Bean 定义覆盖功能。 该配置项的作用是允许 Spring 容器在发现多个相同名称的 Bean 定义时,使用后定义的 Bean 替换已存在的 Bean,而非抛出异常阻止应用启动。此行为适用于开发阶段或某些特定场景,例如集成第三方库时需要自定义某些 Bean 的实现。 此配置可通过以下方式添加到 Spring Boot 的配置文件中: ```properties spring.main.allow-bean-definition-overriding=true ``` 或在 `application.yml` 中配置: ```yaml spring: main: allow-bean-definition-overriding: true ``` 启用该配置后,Spring 将按照 Bean 定义的顺序进行注册,并在遇到重复名称的 Bean 时进行替换。需要注意的是,虽然此方式可解决 Bean 冲突问题,但应谨慎使用,避免因 Bean 被意外覆盖而导致运行时行为异常[^1]。 ### Bean 定义冲突的替代解决方案 除了启用 Bean 定义覆盖机制外,还可以通过重命名 Bean 或使用 `@Primary` 注解明确指定首选 Bean 来避免冲突。例如,在自定义配置类中为 Bean 方法添加 `@Primary` 注解,以确保其优先于其他同名 Bean 被注册: ```java @Bean @Primary public MyService myService() { return new CustomMyService(); } ``` 此外,也可以通过 `@Qualifier` 注解在注入时指定具体的 Bean 名称,从而避免自动装配时的歧义性问题[^3]。 ### Bean 定义覆盖机制的原理 SpringBean 定义覆盖机制由 `BeanDefinitionRegistry` 控制,默认情况下不允许重复注册相同名称的 Bean。该行为由 `allowBeanDefinitionOverriding` 标志位控制。当该标志位被设置为 `true` 时,Spring 将允许后续的 Bean 定义替换已存在的定义,从而实现 Bean 的动态覆盖[^4]。 ### 示例配置 以下是一个完整的 `application.properties` 示例,展示如何启用 Bean 定义覆盖: ```properties # 启用 Bean 定义覆盖 spring.main.allow-bean-definition-overriding=true ``` ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值