javaspringspring-bootclassloading

How Spring invokes @Bean methods if @Configuration class contains unresolvable class refs


Spring can parse @Configuration classes using ClassReaders

Assuming that we have the following scenario

We have an autoconfiguration class with multiple @Bean definitions

One of the @Bean has all conditions passed while second @Bean have @ConditionalOnClass & the class is not present in the class path

@Configuration
class CustomConfiguration {
  @Bean
  @ConditionalOnClass(String.class)
  String knownClass() {
    return "Hello";
  }

  @Bean
  @ConditionalOnClass(MissingClass.class)
  MissingClass secondBean() {
    return new MissingClass();
  }
}

In this scenario, I have couple of questions

  1. Does Spring Boot AutoConfiguration register the first bean into the ApplicationContext?
  2. If (1) is true, will my breakpoint inside first @Bean method be hit during debug
  3. If (2) is true, how is the *AutoConfiguration class get loaded into JVM as this class will refers to other classes (from second @Bean) which cant be resolved at class load time
  4. If (2) is false, does spring generate a class at runtime with just the first @Bean method and invoke the method?

Thanks


Solution

  • Generally speaking you should avoid using @ConditionalOnClass on a @Bean method for this exact reason. This situation is covered in the reference documentation where using a separate @Configuration class to isolate the @ConditionalOnClass condition is recommended.

    To answer your specific questions:

    1. Yes, the first bean will be registered as long as the configuration class can be loaded
    2. Yes, a breakpoint in the first @Bean method should be hit during debugging
    3. It depends on how the class that cannot be resolved is referenced. If it's only used within the body of a @Bean method, the class should load successfully. If the unresolvable class is used in the signature of a @Bean method (typically the return type), the class will fail to load.
    4. N/A

    As noted in the documentation linked to above, rather than worrying about the scenarios described in 3 and what will and will not work, using a separate, probably nested @Configuration class with a class-level condition is the recommended approach. For your specific example, that would look like this:

    @Configuration
    class CustomConfiguration {
    
      @Bean
      @ConditionalOnClass(String.class)
      String knownClass() {
        return "Hello";
      }
    
      @Configuration
      @ConditionalOnClass(MissingClass.class)
      static class DoubtfulBeanConfiguration {
    
        @Bean
        MissingClass missingClass() {
          return new MissingClass();
        }
    
      }
    
    }