Generic Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Introduction

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

In this tutorial, we'll unravel this issue and see how best to address it.

2. Maven Dependencies

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

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

3. Bean Overriding

Spring beans are identified by their names within an ApplicationContext.

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

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.

4. Configuration Change for 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.

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

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

5. Identifying the Beans in Conflict

Let's create two different Spring configurations, each with a testBean() method, to produce the 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 will create our Spring Boot test class:

@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:

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:

Invalid bean definition with name 'testBean' ...

And 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.

6. Possible Solutions

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

Therefore, the first possible solution is to rename our beans.

There are some common ways to set bean names in Spring.

6.1. Changing Method Names

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

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

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

6.2. @Bean Annotation

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

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

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

6.3. Stereotype Annotations

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:

@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

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

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

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

To enable bean overriding, let's set the spring.main.allow-bean-definition-overriding property to true in our application.properties file:

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

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

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

7. Conclusion

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

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

Generic bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Mike
Mike
5 months ago

spring.main.allow-bean-definition-overriding=true
That property does not work for me. Have you verified in a non-test situation? As I step through the code I do not see the boolean being overridden. I dont even see it being set.

Loredana Crusoveanu
5 months ago
Reply to  Mike

Hi Mike,
spring.main.allow-bean-definition-overriding is part of the Spring core properties (https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html), so it would be weird if it didn’t work.
Also, it’s not clear what boolean are you referring to. Can you tell, how do you set the property?

Thanks.

Comments are closed on this article!