The BeanDefinitionOverrideException in Spring Boot – Spring Boot中的BeanDefinitionOverrideException

最后修改: 2020年 2月 13日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

The Spring Boot 2.1 upgrade surprised people with unexpected occurrences of the BeanDefinitionOverrideException. It can confuse developers, and make them wonder what happened to the bean overriding behavior in Spring.

Spring Boot 2.1的升级让人们惊讶于BeanDefinitionOverrideException的意外发生。这可能会让开发人员感到困惑,并让他们想知道Spring中的Bean覆盖行为发生了什么。

In this tutorial, we’ll unravel this issue, and learn how best to address it.

在本教程中,我们将解开这个问题,并学习如何最好地解决它。

2. Maven Dependencies

2.Maven的依赖性

For our example Maven project, we need to add the Spring Boot Starter dependency:

对于我们的示例Maven项目,我们需要添加Spring Boot Starter依赖项。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.3.3.RELEASE</version>
</dependency>

3. Bean Overriding

3.Bean重写

Spring beans are identified by their names within an ApplicationContext.

ApplicationContext中,Spring Bean由它们的名字来识别。

Therefore, bean overriding is a default behavior that happens when we define a bean within an ApplicationContext that has the same name as another bean. It works by simply replacing the former bean in case of a name conflict.

因此,bean overriding是一种默认行为,当我们在ApplicationContext中定义一个与另一个bean同名的bean时发生的。它的作用是在名称冲突的情况下简单地替换前一个Bean。

Starting in Spring 5.1, the BeanDefinitionOverrideException was introduced to allow developers to automatically throw the exception to prevent any unexpected bean overriding. By default, the original behavior is still available, which allows bean overriding.

从Spring 5.1开始,引入了BeanDefinitionOverrideException,允许开发者自动抛出异常,以防止任何意外的bean覆盖。默认情况下,原来的行为仍然可用,它允许bean覆盖。

4. Configuration Change for Spring Boot 2.1

4.针对Spring Boot 2.1的配置变更

Spring Boot 2.1 disabled bean overriding by default as a defensive approach. The main purpose is to notice the duplicate bean names in advance to prevent overriding beans accidentally.

Spring Boot 2.1默认禁用Bean overriding,作为一种防御性方法。主要目的是为了提前注意到重复的Bean名称,以防止意外地覆盖Bean

Therefore, if our Spring Boot application relies on bean overriding, it’s very likely to encounter the BeanDefinitionOverrideException after we upgrade the Spring Boot version to 2.1 and later.

因此,如果我们的Spring Boot应用程序依赖于bean overriding,那么在我们将Spring Boot版本升级到2.1及以后的版本后,很可能会遇到BeanDefinitionOverrideException

In the next sections, we’ll look at an example where the BeanDefinitionOverrideException would occur, and then we’ll discuss some solutions.

在接下来的章节中,我们将看一个会发生BeanDefinitionOverrideException的例子,然后我们将讨论一些解决方案。

5. Identifying the Beans in Conflict

5.识别冲突中的Bean

Let’s create two different Spring configurations, each with a testBean() method, to produce the BeanDefinitionOverrideException:

让我们创建两个不同的Spring配置,每个配置都有一个testBean()方法,以产生BeanDefinitionOverrideException:

@Configuration
public class TestConfiguration1 {

    class TestBean1 {
        private String name;

        // standard getters and setters

    }

    @Bean
    public TestBean1 testBean(){
        return new TestBean1();
    }
}
@Configuration
public class TestConfiguration2 {

    class TestBean2 {
        private String name;

        // standard getters and setters

    }

    @Bean
    public TestBean2 testBean(){
        return new TestBean2();
    }
}

Next, we’ll create our Spring Boot test class:

接下来,我们将创建我们的Spring Boot测试类。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {TestConfiguration1.class, TestConfiguration2.class})
public class SpringBootBeanDefinitionOverrideExceptionIntegrationTest {

    @Test
    public void whenBeanOverridingAllowed_thenTestBean2OverridesTestBean1() {
        Object testBean = applicationContext.getBean("testBean");

        assertThat(testBean.getClass()).isEqualTo(TestConfiguration2.TestBean2.class);
    }
}

Running the test produces a BeanDefinitionOverrideException. However, the exception provides us with some helpful information:

运行该测试会产生一个BeanDefinitionOverrideException。然而,这个异常为我们提供了一些有用的信息。

Invalid bean definition with name 'testBean' defined in ... 
... com.baeldung.beandefinitionoverrideexception.TestConfiguration2 ...
Cannot register bean definition [ ... defined in ... 
... com.baeldung.beandefinitionoverrideexception.TestConfiguration2] for bean 'testBean' ...
There is already [ ... defined in ...
... com.baeldung.beandefinitionoverrideexception.TestConfiguration1] bound.

Notice that the exception reveals two important pieces of information.

请注意,这个例外揭示了两个重要的信息。

The first one is the conflicting bean name, testBean:

第一个是冲突的bean名称,testBean

Invalid bean definition with name 'testBean' ...

The second shows us the full path of the configurations affected:

第二个显示我们受影响的配置的完整路径。

... com.baeldung.beandefinitionoverrideexception.TestConfiguration2 ...
... com.baeldung.beandefinitionoverrideexception.TestConfiguration1 ...

As a result, we can see that two different beans are identified as testBean, causing a conflict. Additionally, the beans are contained inside the configuration classes TestConfiguration1 and TestConfiguration2.

结果,我们可以看到两个不同的Bean被识别为testBean,造成了冲突。此外,这些Bean被包含在配置类TestConfiguration1TestConfiguration2中。

6. Possible Solutions

6.可能的解决方案

Depending on our configuration, Spring Beans have default names unless we set them explicitly.

根据我们的配置,Spring Bean有默认的名字,除非我们明确地设置它们。

Thus, the first possible solution is to rename our beans. There are some common ways to set bean names in Spring.

因此,第一个可能的解决方案是重新命名我们的Bean。在Spring中,有一些常见的方法来设置Bean的名字。

6.1. Changing Method Names

6.1.改变方法名称

By default, Spring takes the names of the annotated methods as bean names.

默认情况下,Spring将被注释的方法的名称作为bean名称

Therefore, if we have beans defined in a configuration class, like our example, then simply changing the method names will prevent the BeanDefinitionOverrideException:

因此,如果我们在配置类中定义了Bean,就像我们的例子一样,那么简单地改变方法名就可以防止BeanDefinitionOverrideException

@Bean
public TestBean1 testBean1() {
    return new TestBean1();
}
@Bean
public TestBean2 testBean2() {
    return new TestBean2();
}

6.2. @Bean Annotation

6.2.@Bean 注释

Spring’s @Bean annotation is a very common way of defining a bean.

Spring的@Bean注解是一种非常常见的定义Bean的方式。

So another option is to set the name property of the @Bean annotation:

所以另一个选择是设置name属性的@Bean注解。

@Bean("testBean1")
public TestBean1 testBean() {
    return new TestBean1();
}
@Bean("testBean2")
public TestBean1 testBean() {
    return new TestBean2();
}

6.3. Stereotype Annotations

6.3.定型注释

Another way to define a bean is with stereotype annotations. With Spring’s @ComponentScan feature enabled, we can define our bean names at the class level using the @Component annotation:

另一种定义Bean的方法是使用stereotype注解。在启用Spring的@ComponentScan功能后,我们可以使用@Component注解在类级别上定义我们的Bean名称。

@Component("testBean1")
class TestBean1 {

    private String name;

    // standard getters and setters

}
@Component("testBean2")
class TestBean2 {

    private String name;

    // standard getters and setters

}

6.4. Beans Coming From 3rd Party Libraries

6.4.来自第三方库的Bean

In some cases, it’s possible to encounter a name conflict caused by beans originating from 3rd party spring-supported libraries.

在某些情况下,有可能遇到源于第三方spring支持的库的Bean引起的名称冲突

When this happens, we should attempt to identify which conflicting bean belongs to our application to determine if we can use any of the above solutions.

当这种情况发生时,我们应该尝试确定哪个冲突的Bean属于我们的应用程序,以确定我们是否可以使用上述任何解决方案。

However, if we’re unable to alter any of the bean definitions, then configuring Spring Boot to allow bean overriding can be a workaround.

然而,如果我们无法改变任何Bean定义,那么配置Spring Boot以允许Bean重写可以作为一种变通方法。

To enable bean overriding, we’ll set the spring.main.allow-bean-definition-overriding property to true in our application.properties file:

为了启用Bean Overriding,我们将在application.properties文件中设置spring.main.allow-bean-definition-overriding属性为true

spring.main.allow-bean-definition-overriding=true

By doing this, we’re telling Spring Boot to allow bean overriding without any change to bean definitions.

通过这样做,我们告诉Spring Boot允许在不改变Bean定义的情况下进行Bean重写。

As a final note, we should be aware that it’s difficult to guess which bean will have priority because the bean creation order is determined by dependency relationships that are mostly influenced in runtime. Therefore, allowing bean overriding can produce unexpected behavior unless we know the dependency hierarchy of our beans well enough.

最后,我们应该意识到很难猜测哪个Bean会有优先权,因为Bean的创建顺序是由依赖关系决定的,而依赖关系大多在运行时受到影响。因此,除非我们对Bean的依赖层次结构有足够的了解,否则允许Bean重写会产生意想不到的行为。

7. Conclusion

7.结语

In this article, we explained what BeanDefinitionOverrideException means in Spring, why it suddenly appears, and how to address it after the Spring Boot 2.1 upgrade.

在这篇文章中,我们解释了BeanDefinitionOverrideException在Spring中的含义,为什么它会突然出现,以及在Spring Boot 2.1升级后如何解决。

As always, the complete source code of this article can be found over on GitHub.

一如既往,本文的完整源代码可以在GitHub上找到