Summary

After upgrading from Spring Boot 3.4.6 to 3.5.0, a configuration class that previously worked now fails with a BeanDefinitionOverrideException due to a duplicate bean definition. The issue appears to be related to how @ConditionalOnMissingBean is evaluated when multiple @Bean methods with the same name exist in the same configuration class.

Sample Configuration

@Configuration(proxyBeanMethods = false)
@EnableMongoAuditing(auditorAwareRef = "mongoAuditorAware")
public class AuditConfiguration {

  @Bean(name = "mongoAuditorAware")
  @ConditionalOnClass(name = "org.springframework.security.core.context.SecurityContextHolder")
  public AuditorAware<?> mongoAuditorAware(...) {
    ...
  }

  @Bean(name = "mongoAuditorAware")
  @ConditionalOnMissingBean
  public AuditorAware<String> mongoWithoutSpringSecurityAuditorAware(...) {
    ...
  }
}

Behavior

✅ In 3.4.6: Only one bean is registered depending on the presence of the class and the condition. ❌ In 3.5.0: Both methods are evaluated, and a mongoAuditorAware bean is defined twice, causing a failure.

Expected Behavior

@ConditionalOnMissingBean should prevent the second bean from being registered if one with the same name already exists or is about to be registered.

Workaround

Changing the annotation to:

@ConditionalOnMissingBean(name = "mongoAuditorAware")

resolves the issue, but this was not required in previous versions.

Analysis

This change in behavior is not documented in the 3.5.0 release notes. There is no related issue or pull request indicating this was an intentional change. It may be a regression or an unintended side effect of internal changes to condition evaluation.

Request

Please clarify whether this is an intentional change in Spring Boot 3.5.0. If so, it would be helpful to document it as a breaking change. Otherwise, it may be worth investigating as a regression.

Comment From: wilkinsona

if one with the same name already exists or is about to be registered

This isn't how condition evaluation works. Bean-based conditions can only consider the state of the bean factory at the time the condition is evaluated, they cannot predict the future so cannot act upon a bean that "is about to be registered".

The change in behavior that you have observed could be due to mongoWithoutSpringSecurityAuditorAware being processed before mongoAuditorAware. @Bean methods should be processed in a consistent order (the order they appear in the bytecode) for the same binaries. This order could change depending on the output of the compiler as I don't think it's specified.

Regardless of whether this is the cause of the problem, you can make your application more robust by using @Import to import two@Configuration classes. The first should define mongoAuditorAware and the second should define mongoWithoutSpringSecurityAuditorAware. Both should be positioned such that they're not found by component scanning.

Another possibility is that it's the generics that are causing the problem but I don't recall any specific changes in this area. If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

Comment From: oburgosm

Thank you very much for your quick and detailed response.

Following your suggestion, I created a minimal Spring Boot application to isolate the issue, and in that simplified setup, everything works as expected — the conditional bean registration behaves correctly in 3.5.0.

This leads me to believe that the problem in my original application is indeed related to the order in which the bean methods are processed, as you pointed out. I’ll investigate further in that direction.

Thanks again for your time and support!

Comment From: Nico-Strecker

Hey, I have a similar problem with Springdoc and Resilience4j, @wilkinsona.

Springdoc:


@Bean
@Lazy(false)
SpringDocParameterNameDiscoverer localSpringDocParameterNameDiscoverer() {
    return new SpringDocParameterNameDiscoverer();
}

Resilience4j:

@Bean
@ConditionalOnMissingBean
public ParameterNameDiscoverer parameterNameDiscoverer() {
    return configuration.parameterNameDiscoverer();
}

The app deploys fine as a -in-development version in our development environment. However, when pushing the same code to our release branch (only the version number in the pom.xml is changed — no -SNAPSHOT), the application fails with the following exception:

APPLICATION FAILED TO START
Description:

Parameter 1 of method spelResolver in io.github.resilience4j.springboot3.spelresolver.autoconfigure.SpelResolverConfigurationOnMissingBean required a single bean, but 2 were found:
- parameterNameDiscoverer: defined by method 'parameterNameDiscoverer' in class path resource [io/github/resilience4j/springboot3/spelresolver/autoconfigure/SpelResolverConfigurationOnMissingBean.class]
- localSpringDocParameterNameDiscoverer: defined by method 'localSpringDocParameterNameDiscoverer' in class path resource [org/springdoc/core/configuration/SpringDocConfiguration.class]
I would have expected that @ConditionalOnMissingBean is evaluated after all other beans are registered, so that parameterNameDiscoverer() from Resilience4j is only created if no other ParameterNameDiscoverer exists.

However, what seems to happen is that both beans are created.

Is there something I'm misunderstanding? This was never an issue in Spring Boot 3.4.6, but in 3.5.0 the behavior seems to have changed and my code is now unstable.

I can't directly control either library and will raise issues if needed for them, but in my opinion, they work together correctly. I just want to clarify this behavior first, might it be a spring boot 3.5.0 issue?