Technical Briefing
In Spring Boot 2.x, defining two beans with the same name caused the second to silently replace the first โ a behavior that could hide serious misconfiguration. Spring Boot 3 disables this by default, converting silent overrides into a hard startup failure: BeanDefinitionOverrideException. This is a deliberate safety improvement that surfaces hidden bean conflicts during migration or when library auto-configurations clash with application beans.
โ Signal Detected
org.springframework.beans.factory.support.BeanDefinitionOverrideException: Invalid bean definition with name 'dataSource' defined in com.example.config.DataSourceConfig: Cannot register bean definition [Root bean: class [com.zaxxer.hikari.HikariDataSource]] since there is already [Root bean: class [org.apache.tomcat.jdbc.pool.DataSource]] bound. Action: Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
โ Trace Analysis
Multiple @Configuration classes are producing @Beans with the same name โ either because the application defines a bean that conflicts with a library's auto-configured bean, or because two configurations in a large codebase accidentally share a bean name. Spring Boot 3's stricter default makes this an application startup failure rather than a silent runtime surprise.
โฆ Remediation Plan
Rename the conflicting bean with a unique, descriptive name (preferred: always the first option).
Use @ConditionalOnMissingBean so your custom bean is only registered if no other bean of that type exists.
Use @Primary to signal which bean should be injected by default when multiple candidates exist.
Add @Qualifier to injection sites to select a specific bean by name.
Setting spring.main.allow-bean-definition-overriding=true restores Boot 2 behavior but is a last resort โ it re-enables silent overrides.
// โ CONFLICT โ two beans named 'dataSource' at startup class="hi-ann">@Configuration public class AppConfig { class="hi-ann">@Bean public DataSource dataSource() { // Name: "dataSource" return new HikariDataSource(hikariConfig()); } } // โ FIX 1: Unique naming class="hi-ann">@Configuration public class AppConfig { class="hi-ann">@Bean("appDataSource") public DataSource dataSource() { ... } } // โ FIX 2: @ConditionalOnMissingBean (Library-safe override pattern) class="hi-ann">@Configuration public class AppConfig { class="hi-ann">@Bean class="hi-ann">@ConditionalOnMissingBean(DataSource.class) // Only register if nothing else provides one public DataSource dataSource() { ... } } // โ FIX 3: @Primary (Preferred injection candidate) class="hi-ann">@Bean class="hi-ann">@Primary public DataSource primaryDataSource() { ... } class="hi-ann">@Bean("secondaryDataSource") public DataSource secondaryDataSource() { ... } // Injection uses @Primary automatically: // @Autowired DataSource ds; โ gets primaryDataSource // โ ๏ธ ESCAPE HATCH (Boot 2 compatibility only โ avoid in new code) # application.properties spring.main.allow-bean-definition-overriding=true
โ Engineering Deep-Dive
Bean Naming Rules
By default, a class="hi-ann">@Bean method's name is the method name. A class annotated with class="hi-ann">@Component, class="hi-ann">@Service, or class="hi-ann">@Repository uses the decapitalized class name. When two beans share the same name in the same ApplicationContext, Boot 3 fails fast at startup rather than allowing a random winner.
Auto-Configuration Conflicts
The most common source of this exception in Boot 3 migrations is the application defining a bean that Boot's own auto-configuration also tries to provide โ e.g., a custom DataSource clashing with DataSourceAutoConfiguration. Use class="hi-ann">@ConditionalOnMissingBean in your configuration to yield to auto-configuration, or use spring.autoconfigure.exclude to disable the conflicting auto-configuration entirely.
Bean Naming Conventions at Scale
In large codebases with many shared modules, prefix bean names with the module name to avoid collisions: class="hi-ann">@Bean("payments.dataSource"), class="hi-ann">@Bean("inventory.dataSource"). This is especially important when composing multiple Spring modules into a single application context.
โ Elite Standards
Engineering Rule
Never use spring.main.allow-bean-definition-overriding=true in new projects โ it trades a startup error for a silent runtime misconfiguration.
Engineering Rule
Prefer @ConditionalOnMissingBean in all shared library configurations to make them override-safe by design.
Engineering Rule
Add a startup smoke test to your CI pipeline that verifies ApplicationContext loads successfully โ catches bean conflicts before they reach production.
FAQ
- What causes BeanDefinitionOverrideException in Boot 3 in Spring Boot 3?
- Multiple @Configuration classes are producing @Beans with the same name โ either because the application defines a bean that conflicts with a library's auto-configured bean, or because two configurations in a large codebase accidentally share a bean name. Spring Boot 3's stricter default makes this an application startup failure rather than a silent runtime surprise.
- How do I fix BeanDefinitionOverrideException in Boot 3?
- Rename the conflicting bean with a unique, descriptive name (preferred: always the first option). Use @ConditionalOnMissingBean so your custom bean is only registered if no other bean of that type exists. Use @Primary to signal which bean should be injected by default when multiple candidates exist. Add @Qualifier to injection sites to select a specific bean by name. Setting spring.main.allow-bean-definition-overriding=true restores Boot 2 behavior but is a last resort โ it re-enables silent overrides.
- Best practice #1 for preventing Spring Context ยท Boot 3 errors?
- Never use spring.main.allow-bean-definition-overriding=true in new projects โ it trades a startup error for a silent runtime misconfiguration.
- Best practice #2 for preventing Spring Context ยท Boot 3 errors?
- Prefer @ConditionalOnMissingBean in all shared library configurations to make them override-safe by design.
- Best practice #3 for preventing Spring Context ยท Boot 3 errors?
- Add a startup smoke test to your CI pipeline that verifies ApplicationContext loads successfully โ catches bean conflicts before they reach production.