This is a bit hard to put into words properly, but consider the following chain:
AController has @ConditionalOnBean(BService.class)
BService has @ConditionalOnBean(OtherService.class)
OtherService has @ConditionalOnProperty("dum") ---> will evaluate to false
OtherController which is unrelated
in my test case, I just start the context using mockmvc and test OtherController, but context startup fails with:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'AController' defined in file [AController.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'BService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
what I expect is that AController is never constructed because it requires BService which requires OtherService which has a condition that evaluates to false.
Now the funny thing, if I change the names of my controller and service, it works.
BController has @ConditionalOnBean(AService.class)
AService has @ConditionalOnBean(OtherService.class)
My theory is that since now AService is evaluated before BController in ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass, it is removed and when BController is evaluated, its expression properly evaluates to false. For the failing case I think AController is evaluated before BService, finds the bean definition for BService and thus evaluates to true, but in the next step BService is evaluated to false (since OtherService is not present) and is removed from the list of bean names.
I uploaded my minimal reproducible example here https://github.com/MiniDigger/spring-boot-issue
Tested on Spring Boot 2.7.1, 2.7.6 and 3.0.0 (apologies if this is not a boot but a framework problem, I can never tell the difference, autoconfiguration is a boot thing so I opened it here)
Comment From: bclozel
This is the expected behavior. As you've found out, such condition implementations rely on ordering and are not meant to be used on regular @Configuration classes or beans. You should only use them on ordered auto-configuration classes. This is explained in the reference documentation and in the Javadoc.