Lets have this configuration class. I removed all the bean-method content because its not needed for the issue description.

@Configuration
public class CreationCacheConfiguration {

  // this whole configuration should only be used if the property 'backend.type' is 'JDBC'
  @Configuration
  @ConditionalOnProperty(name = "backend.type", havingValue = "JDBC")
  @EnableJpaRepositories(
    entityManagerFactoryRef = "creationCacheEntityManager",
    transactionManagerRef = "creationCacheTransactionManager",
    basePackages = "cache.backend.jdbc")
  @EnableTransactionManagement
  class ConfigurationCreationCacheBackendJdbcConfiguration {
    @Bean(name = "creationCacheDbDataSource", destroyMethod = "close")
    DataSource creationCacheDbDataSource(final ConfigurationCacheBackendProperties properties) {
      ...
    }
    @Bean(name = "creationCacheEntityManager")
    LocalContainerEntityManagerFactoryBean creationCacheEntityManager(
        final @Qualifier("creationCacheDbDataSource") DataSource dataSource) {
      ...
    }
    @Bean(name = "creationCacheTransactionManager")
    PlatformTransactionManager creationCacheTransactionManager(
        @Qualifier("creationCacheEntityManager") final EntityManagerFactory creationCacheEntityManager) {
      ...
    }
    @Bean
    ConfigurationCreationCacheJpaRepositoryFacade configurationCreationCacheJpaRepositoryFacade(
        final ConfigurationCreationCacheJpaRepository cacheEntryRepository) {
      ...
    }
    @Bean
    ConfigurationCreationCacheBackend configurationCreationCacheBackendJdbc(
        final ConfigurationCacheBackendProperties props,
        final ConfigurationCreationCacheJpaRepositoryFacade configurationCreationCacheJpaRepositoryFacade) {
      ...
    }

    // here I embedded this configuration with my expectation that it will only be used if the surrounding 
    // configuration 'backend.type=JDBC' is enabled AND health-indicator 'ccCreationCacheDb' is enabled
    @Configuration
    @ConditionalOnEnabledHealthIndicator("ccCreationCacheDb")
    class HealthContributorConfiguration {

     // and another embedded configuration ... like the surrounding but now with another condition 
     // 'backend.jdbc.health.async.enabled' is 'true'
     // if this class is "static"  the configuration is always enabled (bug?) and spring try to create the
     // bean 'ccCreationCacheDbHealthIndicator' ... but without luck because  the DataSource 'creationCacheDbDataSource'
     // doesn't exists. To me it seems that embedded static configuration class leave somehow the context of the surrounding
     // configuration class 
     // I'm using static classes very often and until yet I was not aware of this effect :-) Maybe because of the nested structure
      @Configuration
      @ConditionalOnProperty(name = "backend.jdbc.health.async.enabled", havingValue = "true")
      static class AsyncHealthContributorConfiguration {
        @Bean(name = "ccCreationCacheDbHealthScheduler", destroyMethod = "shutdown")
        AsyncDatasourceHealthIndicatorSchedulingThreadPoolExecutor ccCreationCacheDbHealthScheduler(
            final ConfigurationCacheBackendJdbcHealthAsyncIndicatorProperties properties) {
          ...
        }
        @Bean("ccCreationCacheDbHealthIndicator")
        HealthIndicator ccCreationCacheDbHealthIndicator(
            final ConfigurationCacheBackendProperties properties,
            final ConfigurationCacheBackendJdbcHealthAsyncIndicatorProperties creationCacheJdbcHealthAsyncIndicatorProperties,
            final @Qualifier("creationCacheDbDataSource") DataSource dataSource,
            final @Qualifier("ccCreationCacheDbHealthScheduler") AsyncDatasourceHealthIndicatorSchedulingThreadPoolExecutor ccCreationCacheDbHealthScheduler,
            final ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
          ...
        }
      }
      @ConditionalOnMissingBean(name = "ccCreationCacheDbHealthIndicator")
      @Bean("ccCreationCacheDbHealthIndicator")
      HealthIndicator ccCreationCacheDbHealthIndicator(
          final ConfigurationCacheBackendProperties properties,
          final @Qualifier("creationCacheDbDataSource") DataSource dataSource,
          final ObjectProvider<DataSourcePoolMetadataProvider> metadataProviders) {
        ...
      }
      @Nullable
      static String getValidationQuery(final DataSourcePoolMetadataProvider poolMetadataProvider, final DataSource source) {
        final DataSourcePoolMetadata poolMetadata = poolMetadataProvider.getDataSourcePoolMetadata(source);
        return (poolMetadata != null) ? poolMetadata.getValidationQuery() : null;
      }
    }
  }
}

The error from spring context is this:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ccCreationCacheDbHealthIndicator' defined in class path resource [XXX/cc/rest/spring/CreationCacheConfiguration$ConfigurationCreationCacheBackendJdbcConfiguration$HealthContributorConfiguration$AsyncHealthContributorConfiguration.class]: Unsatisfied dependency expressed through method 'ccCreationCacheDbHealthIndicator' parameter 2: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier("creationCacheDbDataSource")}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:800)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:550)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1332)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1162)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:560)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:520)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:973)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:942)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:608)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:734)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:436)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:312)
    at org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137)
    at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58)
    at org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46)
    at org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1406)
    at org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:545)
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137)
    at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:187)
    at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:119)
    ... 65 common frames omitted
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier("creationCacheDbDataSource")}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1824)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1383)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1337)
    at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:888)
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)
    ... 90 common frames omitted

I'm using springboot 3.1.3

Comment From: wilkinsona

Thanks for the report. Unfortunately it's hard to tell what's going on from a rather complex yet incomplete example.

A couple of things that may or may not be related:

  1. Generally speaking nested @Configuration classes should be static
  2. Your use of @ConditionalOnMissingBean is unlikely to work correctly as it appears to be relying on some ordering that isn't guaranteed

If you would like us to spend some more time investigating, please spend some time providing a complete yet minimal sample that reproduces the problem. You can share it with us by pushing it to a separate repository on GitHub or by zipping it up and attaching it to this issue.

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: ahoehma

I will try to reproduce the effect in an example.

Comment From: ahoehma

I made a simple demo project and I can't reproduce the effect there. Which is a good sign for the spring-framework. So I guess I have somewhere else in my code a hidden feature ;) I attached the demo.zip may someone will find it interesting.

package com.example.demo;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DemoConfiguration {
  /**
   * This whole configuration should only be used if the property 'demo.foo' is 'foo'.
   */
  @Configuration
  @ConditionalOnProperty(name = "demo.foo", havingValue = "foo")
  class DemoFooConfiguration {
    @Bean("demoService")
    DemoService demoService() {
      System.out.println("demo-service created");
      return () -> "hello";
    }
    /**
     * Embedded configuration.
     *
     * my expectation will only been used if
     *
     * 1. the surrounding configuration 'demo.foo=foo' is true
     *
     * 2. AND health-indicator 'demo' is enabled
     */
    @Configuration
    @ConditionalOnEnabledHealthIndicator("demo")
    class DemoHealthConfiguration {
      /**
       * Another embedded configuration ... like the surrounding but now with another condition 'demo.bar=bar' is true.
       */
      @Configuration
      @ConditionalOnProperty(name = "demo.bar", havingValue = "bar")
// 
// could be static or not  ... works!
//
      class SubConfiguration {
        @Bean("demoHealthIndicator")
        HealthIndicator demoHealthIndicator(final @Qualifier("demoService") DemoService demoService) {
          System.out.println("demo-health (bar) created");
          return new DemoHealthIndicator();
        }
      }
      @ConditionalOnMissingBean(name = "demoHealthIndicator")
      @Bean("demoHealthIndicator")
      HealthIndicator demoHealthIndicator(final @Qualifier("demoService") DemoService demoService) {
        System.out.println("demo-health (!bar) created");
        return new DemoHealthIndicator();
      }
    }
  }
}

package com.example.demo;

import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

class DemoApplicationTests {
  @SpringBootTest(properties = {
      "demo.foo=foo",
      "management.health.demo.enabled=true",
      "demo.bar=bar",
  })
  @Nested
  class Test1 {
    @Test
    void contextLoads() {
    }
  }
  @SpringBootTest(properties = {
      "demo.foo=foo",
      "management.health.demo.enabled=true",
      "demo.bar=nonono",
  })
  @Nested
  class Test2 {
    @Test
    void contextLoads() {
    }
  }
  @SpringBootTest(properties = {
      "demo.foo=foo",
      "management.health.demo.enabled=false",
  })
  @Nested
  class Test3 {
    @Test
    void contextLoads() {
    }
  }
  @SpringBootTest(properties = {
      "demo.foo=nonono",
      "management.health.demo.enabled=false",
  })
  @Nested
  class Test4 {
    @Test
    void contextLoads() {
    }
  }
}

SpringBoot Nested static configuration class doesn't use the context of the surrounding configuration class

17:02:49.402 [main] INFO org.springframework.test.context.support.AnnotationConfigContextLoaderUtils -- Could not detect default configuration classes for test class [com.example.demo.DemoApplicationTests$Test1]: Test1 does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
17:02:49.577 [main] INFO org.springframework.boot.test.context.SpringBootTestContextBootstrapper -- Found @SpringBootConfiguration com.example.demo.DemoApplication for test class com.example.demo.DemoApplicationTests$Test1

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.3)

2023-09-09T17:02:50.383+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test1      : Starting DemoApplicationTests.Test1 using Java 17.0.4 with PID 2404 (started by hoehmann in D:\Dev\git\privat\demo)
2023-09-09T17:02:50.386+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test1      : No active profile set, falling back to 1 default profile: "default"
demo-service created
demo-health (bar) created
2023-09-09T17:02:52.466+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test1      : Started DemoApplicationTests.Test1 in 2.613 seconds (process running for 4.298)
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2023-09-09T17:02:54.408+02:00  INFO 2404 --- [           main] t.c.s.AnnotationConfigContextLoaderUtils : Could not detect default configuration classes for test class [com.example.demo.DemoApplicationTests$Test2]: Test2 does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
2023-09-09T17:02:54.412+02:00  INFO 2404 --- [           main] .b.t.c.SpringBootTestContextBootstrapper : Found @SpringBootConfiguration com.example.demo.DemoApplication for test class com.example.demo.DemoApplicationTests$Test2

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.3)

2023-09-09T17:02:54.498+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test2      : Starting DemoApplicationTests.Test2 using Java 17.0.4 with PID 2404 (started by hoehmann in D:\Dev\git\privat\demo)
2023-09-09T17:02:54.499+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test2      : No active profile set, falling back to 1 default profile: "default"
demo-service created
demo-health (!bar) created
2023-09-09T17:02:55.075+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test2      : Started DemoApplicationTests.Test2 in 0.648 seconds (process running for 6.908)
2023-09-09T17:02:55.088+02:00  INFO 2404 --- [           main] t.c.s.AnnotationConfigContextLoaderUtils : Could not detect default configuration classes for test class [com.example.demo.DemoApplicationTests$Test3]: Test3 does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
2023-09-09T17:02:55.093+02:00  INFO 2404 --- [           main] .b.t.c.SpringBootTestContextBootstrapper : Found @SpringBootConfiguration com.example.demo.DemoApplication for test class com.example.demo.DemoApplicationTests$Test3

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.3)

2023-09-09T17:02:55.170+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test3      : Starting DemoApplicationTests.Test3 using Java 17.0.4 with PID 2404 (started by hoehmann in D:\Dev\git\privat\demo)
2023-09-09T17:02:55.171+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test3      : No active profile set, falling back to 1 default profile: "default"
demo-service created
2023-09-09T17:02:55.643+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test3      : Started DemoApplicationTests.Test3 in 0.534 seconds (process running for 7.477)
2023-09-09T17:02:55.656+02:00  INFO 2404 --- [           main] t.c.s.AnnotationConfigContextLoaderUtils : Could not detect default configuration classes for test class [com.example.demo.DemoApplicationTests$Test4]: Test4 does not declare any static, non-private, non-final, nested classes annotated with @Configuration.
2023-09-09T17:02:55.660+02:00  INFO 2404 --- [           main] .b.t.c.SpringBootTestContextBootstrapper : Found @SpringBootConfiguration com.example.demo.DemoApplication for test class com.example.demo.DemoApplicationTests$Test4

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v3.1.3)

2023-09-09T17:02:55.729+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test4      : Starting DemoApplicationTests.Test4 using Java 17.0.4 with PID 2404 (started by hoehmann in D:\Dev\git\privat\demo)
2023-09-09T17:02:55.730+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test4      : No active profile set, falling back to 1 default profile: "default"
2023-09-09T17:02:56.192+02:00  INFO 2404 --- [           main] c.e.demo.DemoApplicationTests$Test4      : Started DemoApplicationTests.Test4 in 0.516 seconds (process running for 8.026)

demo.zip

Comment From: bclozel

Let's close this issue for now then. We can reopen if this can be reproduced.