Using an interface with two bean implementations.
One implementation has a conditional.
I want them to be mutually exclusive, so that when one bean is created, other is not, resulting in a single bean implementing interface.
Fallback bean uses @ConditionalOnMissingBean
to check if conditional bean is registered.
Based on configuration classes processing order it may work as expected or result in a missing bean.
The following is the behavior resulting in missing bean: 1. conditional bean definition is registered 2. fallback bean conditional is evaluated and fails because it founds the previous bean definition 3. conditional bean is processed and conditional fails resulting in removing the bean definition
If steps 2 and 3 are reversed, then fallback bean is registered successfully, as expected.
Sample repository: https://github.com/cdalexndr/spring-boot-issue-24227
Note that moving all classes in the same package results in working test (different configuration class processing order).
Comment From: snicoll
@cdalexndr this is working as advertized. The Javadoc of @ConditionalOnMissingBean
states:
The condition can only match the bean definitions that have been processed by the application context so far and, as such, it is strongly recommended to use this condition on auto-configuration classes only. If a candidate bean may be created by another auto-configuration, make sure that the one using this condition runs after.
Using it with classpath scanning is not a great idea as the order can't be enforced. Please review the Javadoc of the condition and adapt your configuration accordingly.
Comment From: cdalexndr
Instead of hiding this issue under docs, it would be nice if this limitation was resolved so it helps developers to not make invalid configurations.
As the conditional bean definition is registered, spring knows about the bean existence, but it is handled incorrectly.
I think that some deferred evaluation could solve this issue, and some dependency processing so that all beans of the required type are processed before evaluating @ConditionalOnMissingBean
.
Comment From: snicoll
There is already a deferred evaluation, your use case is super simple where the type is declared on the type but there are situations where a bean of the target type is contributed programmatically and we have to handle that too.
At the end of the day, making it transparent for a one to one use case is not worth it IMO. First of all, waiting for all the bean definitions to be created to run conditions is a bit naive as you would potentially process a very large amount of bean definitions just for removing them once the condition evaluates. Then, your use case with several candidates would require you to order them anyway to express a preference amongst the candidates.
it is an issue for you because you have to tell something extra to the container, but what we require from you is advertized in the documentation. This documentation also indicates that you're not supposed to use this condition on user configuration.