I ran into this issue while implementing an auto-configuration in a complex project, but here I will exemplify with a very simple configuration class. In the real case I also have @ConditionalOnMissingBean on both beans.

Below, BeanB can be enabled through a property and then BeanA depends on BeanB. Depending on the property, I should get either both beans or none at all.

@Configuration
public class ExampleConfiguration {

    @Bean
    @ConditionalOnBean(BeanB.class)
    BeanA beanA(BeanB beanB) {
        return new BeanA(beanB);
    }

    @Bean
    @ConditionalOnProperty("b.enabled")
    BeanB beanB() {
        return new BeanB();
    }
}

In practice BeanA will only be created if it is declared below BeanB in the configuration. As it it above, it will never be created.

I am (now) aware of the Javadoc fragment below, but still, when the two beans are defined in the same configuration it should be possible to make it work as expected, irrespective of the order in the file. I doubt that many developers would notice that the fragment above is not supposed to work and troubleshooting the issue in a complex project would be quite frustrating.

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.

I also have a repo that shows the issue: https://github.com/bcalmac/conditional-on-bean-order

Comment From: bcalmac

Also, is it even guaranteed that defining the beans in the expected order will work across JVMs and Java versions?

Comment From: snicoll

when the two beans are defined in the same configuration it should be possible to make it work as expected, irrespective of the order in the file.

Two beans in the same configuration is just one instance of the ordering that is mentioned in the Javadoc. Also, guarding a bean if BeanB is available while creating BeanB in the very same file is a bit odd, you may want to review that condition arrangement. The condition applies to what is available in the BeanFactory when the condition is evaluated so there's nothing we can do about it.

Also, is it even guaranteed that defining the beans in the expected order will work across JVMs and Java versions?

If you use classpath scanning, no (and you shouldn't). Import, AutoConfigureBefore AutoConfigureAfter, and AutoConfigureOrder provide explicit ordering and should be used for this.

Comment From: bcalmac

Thank you @snicoll for the clarification. However I'm not sure I understand this part:

Also, is it even guaranteed that defining the beans in the expected order will work across JVMs and Java versions?

If you use classpath scanning, no (and you shouldn't). Import, AutoConfigureBefore AutoConfigureAfter, and AutoConfigureOrder provide explicit ordering and should be used for this.

I was referring to the order in which BeanA and BeanB are defined in the same auto-configuration class:

SomeAutoConfiguration {
    @ConditionalOnProperty("b.enabled")
    BeanB

    @ConditionalOnBean(BeanB.class)
    BeanA
}

I could empirically notice that placing BeanB above BeanA in the class will work as expected. BeanB is defined first and @ConditionalOnBean(BeanB.class) is evaluated properly. But then I was wondering whether that is true in general, knowing that Class.getMethods() does not guarantee a particular order.

And if the order is not guaranteed, how would you handle this particular use case when in one auto-configuration file: - one bean (BeanB) is conditionally defined when enabled through configuration properties - another bean (BeanA) depends on the first one and should be defined only when BeanB exists.

An obvious answer would be to put @ConditionalOnProperty("b.enabled") on both beans. However, in the real project that prompted this, BeanB can also be supplied by the application and then BeanA should be instantiated even without "b.enabled=true". The conceptual requirement truly is "define BeanA if and only if BeanB exists".

Also, another workaround would be to put BeanA and BeanB is separate auto-configuration classes and use @AutoConfigureAfter, but that doesn't look nice.

I just want to make sure I'm not missing a better alternative before choosing some workaround.

Comment From: snicoll

I just want to make sure I'm not missing a better alternative before choosing some workaround.

I can see why you see things this way but it isn't a workaround. You are right with Class.getMethods and the right way to handle a condition that depends on the state of the BeanFactory is to order things deterministically. None of the solution that you've described are what I would consider first.

You could group those bean definitions in a nested configuration class and share the condition:

@Configuration(proxyBeanMethods=false)
@ConditionalOnProperty("b.enabled")
static class WhenBIsEnabledConfiguration {

}

The above is nice and materializes concretely what happens when B is enabled. Another solution is to run things in order using @Import.

@Configuration(proxyBeanMethods=false)
@Import(BaseInfrastructureConfigiuration.class, Optionalnfrastructure.class)
public class MyAutoConfiguration { }

Where B might be created by BaseInfrastructureConfiguration and A would react to that by being processed in OptionalInfrastructure.

I hope the above can convince you that it is a far more superior configuration arrangement, irrespective of your original question. If you have further questions, please follow-up on StackOverfow, as mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.

Comment From: SharkFourSix

Also, is it even guaranteed that defining the beans in the expected order will work across JVMs and Java versions?

Are you saying that defining a bean in one JVM can affect another JVM if they are both using a common package?

If so, is there a source?

Comment From: wilkinsona

No. One JVM cannot affect another. However, the ordering that's observed with one JVM is not guaranteed to be the same on anther JVM. Similarly, the ordering that's observed with one version of Java is not guaranteed to be the same with another version of Java. You could order things explicitly (using the mechanisms @snicoll listed above) rather than relying on chance.

If you have any further questions, please follow up on Stack Overflow or Gitter. As mentioned in the guidelines for contributing, we prefer to use GitHub issues only for bugs and enhancements.

Comment From: pbodnar

Trivia: It looks like this has been already discussed, with the same conclusions, back in #7815.