Using Spring v5.3.27 with Spring Boot v2.7.12 and @ConditionalOnBean
annotation for the classes annotated with @Component
I receive non-deterministic beans initialization (presence) depending on the way I run the application. For instance, if I run it via mvn spring-boot:run
my conditions work well, but if I mvn package
a jar and then run it via java -jar ...
then some of the beans get initialized despite that the @ConditionalOnBean
should return false
.
I'm aware of the comment on @ConditionalOnBean
saying that 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.
Though, it's way easier to assign a @Component
annotation on a class and rely on Component Scan rather than writing a lot of wiring code that requires maintenance and increases the complexity of the application.
Thus. I'd like to discuss the limitation and the way to remove it.
My analysis of the relevant business logic implementation led me to the ClassPathScanningCandidateComponentProvider
class and its scanCandidateComponents
method. There, depending on the order of the resources
fetched via getResourcePatternResolver().getResources(packageSearchPath)
, candidates get checked via isCandidateComponent
and isConditionMatch
using MetadataReader
associated with the resource
.
The first idea is to order the resource based on a priority that is based on the presence or absence of the @Conditional
annotations as well as the annotation class. E.g. having @ConditionalOnBean
, @ConditionalOnMissingBean
, and @ConditionalOnSingleCandidate
evaluated with the lowest priority.
I see that the mentioned annotations come from Spring Boot, thus the Condition
interface could be extended to have a method that could be used to get the required metadata for the ordering.
I guess that I'm not the first one who challenged this point, if so, then I'd appreciate sharing your thoughts on why it's all a bad idea and the way it's implemented at the moment is the best one considering all things.
Cheers and thank you for such an amazing and inspiring framework!
Comment From: gallyamb
Not sure, but maybe it's somehow related to the https://github.com/spring-projects/spring-framework/issues/28337
Comment From: Gems
Thank you @gallyamb though it looks like a different problem to me
Comment From: snicoll
Though, it's way easier to assign a @Component annotation on a class and rely on Component Scan rather than writing a lot of wiring code that requires maintenance and increases the complexity of the application.
I would argue relying on component scan and using @ConditionalOnBean
is actually increasing the complexity of the application for the reason described in the Javadoc.
I guess that I'm not the first one who challenged this point, if so, then I'd appreciate sharing your thoughts on why it's all a bad idea and the way it's implemented at the moment is the best one considering all things.
Expecting component scan to give you a reliable order, including when the application evolves is setting the wrong expectation. Even if we managed to provide some sort of ordering, it could easily break in subtle ways, such as with code refactoring: changing the name, package and/or location of a @Component
could lead to breaking conditions. In a nutshell, the comment in the Javadoc remains: if you are using @ConditionalOnMissingBean
for a type, you need to make sure whatever is contributing such type is evaluated first. You could use @Import({A.class, B.class, C.class})
to define such order and that doesn't looks like a lot of code to me.
Comment From: Gems
Thank you for the feedback @snicoll
I'll give the @Import
option for ordering a try and see if it makes the trick. It wasn't intuitively understandable that @Import
would affect and define the order.
On the related matter. Judging by your comments I can see that you consider some sort of alphabetical order or similar, that indeed could be easily unintentionally broken on refactoring.
While I'm proposing an order based on @Conditional
annotations and their potential impact on the initialization of beans. Of course, it most likely requires an extension of the Condition
interface to provide additional metadata for ordering and other related aspects. On the other hand, this approach makes the user configuration cleaner and reduces the likelihood of errors in the tool using.
I would be happy to discuss and contribute further with your support or someone else's, to ensure that the design aligns with the framework's philosophy and other important aspects.
Comment From: snicoll
I understood, I think, what you're heading at but I don't see how an algorithm that orders classpath-detected components could be more intuitive than a well defined order. I don't think it could ever be, and such algorithm would therefore be a way to avoid defining the components that are target for conditions, and rather rely on component scan. My point is that I believe component scan to be wrong tool for defining components that rely on conditions.
That's also why we think such conditions should be applied on auto-configurations only, where they have relative ordering with auto-configure before
and after
.
All in all, I don't agree that this makes the configuration cleaner. Surely there's an intermediate layer that is not needed (the @Configuration
, and the order at which they are processed), but that's precisely the point of understanding exactly in which context the various conditions apply. It's more than a preference in this case, and I don't understand why defining those components is such an hassle considering you need to make sure the conditions evaluate against the right state of the bean factory.
Feel free to investigate on your end and we can continue the discussion based on code as I think it would be easier to argue the merits of one vs. the other.