I have a simple nested autoconfig which loads 1 of the 2 beans depending on the conditional check. Something like
@Configuration
@ConditionalOnWebApplication
public class ServiceAutoConfiguration {
@Configuration
//used if a Undertow is present
@ConditionalOnClass(name = "io.undertow.Undertow")
static class OptionConfiguration {
@Bean
@ConditionalOnMissingBean
public ConfigHelper configHelper() {
return new ConfigHelper("Option 1");
}
}
@Configuration
//used if a Undertow is absent
@ConditionalOnMissingClass(value = "io.undertow.Undertow")
static class DefaultConfiguration {
@Bean
@ConditionalOnMissingBean
public ConfigHelper configHelper() {
return new ConfigHelper("Default");
}
}
static class ConfigHelper {
public ConfigHelper(String val) {
System.out.println("Value= "+ val);
}
}
}
This works great. Now I need to add another conditional bean which needs to be checked depending on a Property set. So I create another nested class in ServiceAutoConfiguration like
@Configuration
//used if a property is explicitly set
static class NoopConfiguration {
@Bean
@ConditionalOnProperty(name = "a.b.c", havingValue = "true")
public ConfigHelper configHelper() {
return new ConfigHelper("NOOP");
}
}
I also add @AutoConfigureAfter(NoopConfiguration.class)
on the existing 2 configuration so that they are loaded only after checking the newly added config.
However the context is loading the bean defined in DefaultConfiguration first and then trying to load the bean defined in NoopConfiguration (as I have defined the property a.b.c=true
) which fails expectedly due to duplicate bean and overriding disabled. I only want 1 of the 3 beans to be instantiated.
Based on my understanding the @AutoConfigureAfter
should order the NoopConfiguration
before the other 2 classes.
interestingly, if I change the name of NoopConfiguration to ANoopConfiguration I get the desired result with ANoopConfiguration getting loaded prior to DefaultConfiguration and no exception occurs. It is not clear why the classes are loaded in alphabetical order irrespective of AutoConfigureAfter
The behavior was working till a while back and suddenly stopped working(with no change to springboot version). is there a way to control the ordering of the auto config class.
The above test is available at https://github.com/ranarula/configureafter
Comment From: spencergibb
It only works on autoconfiguration, nested classes are not. Only one class is listed in spring.factories
Comment From: mbhave
To add to what Spencer said, you can order nested classes using an explicit @Import
. We do that here.
Comment From: ranarula
@mbhave - I tried to use the approach you mentioned but I am still not getting the desired result. The auto-configuration is using the import to get all nest configuration classes in the order
Please refer to the sample at https://github.com/ranarula/configureafter/tree/master/src/main/java/com/example/demo
I don't want to have different names for the resulting ConfigHelper
bean
Comment From: spencergibb
What is the result you get?
Comment From: ranarula
This is what I get, though the intent is to load 1 of the 3 bean in any scenario.
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2020-02-20 21:22:33.141 ERROR 97856 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The bean 'configHelper', defined in class path resource [com/example/demo/ServiceConfiguration$NoopConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [com/example/demo/ServiceConfiguration$DefaultConfiguration.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
In this case since the property a.b.c=true
is defined only bean defined by NoopConfiguration
should have been initialized. But since there is no way to control the ordering of beans, the been defined by DefaultConfiguration
is getting loaded first and then bean defined by NoopConfiguration
I want to first check for the property and try to load the bean via NoopConfiguration
, if not then either of UndertowConfiguration
or DefaultConfiguration
should be loaded.
Comment From: snicoll
@ranarula please follow-up with further questions on StackOverflow or come chat with us on Gitter, as mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.
If you create a StackOverflow question, feel free to edit your description to add a link to it.
Comment From: chrylis
The behavior here is semantically inconsistent. Either the nested classes "are" auto-configuration, and thus should be treated that way, or they "are not", and shouldn't be automatically activated when the containing class is.
Comment From: wilkinsona
I don't think there's any inconsistency here. The nested classes are not auto-configuration and they are not treated as auto-configuration. Instead, they are behaving as any @Configuration
class does when nested beneath a top-level @Configuration
class and are being implicitly imported. That's standard Spring Framework behaviour.
Comment From: chrylis
A rule that says "configuration classes activated because they're nested in an auto-configuration class are not auto-configuration but external configuration classes @Import
ed from an auto-configuration are auto-configuration" is baffling, and I'm someone who's been writing auto-configuration since 0.5.0M6. I don't understand a mental model under which auto-configurationness is inherited by @Import
but not by physically nesting, and this model doesn't seem to be documented.
Comment From: philwebb
Both nested classes and those imported by @Import
are not considered auto-configuration classes. You can't use @AutoConfigureBefore
or @AutoConfigureAfter
annotations with them. Both @Import
and nested classes should work in the same way.