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 @Imported 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.