1

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

Ashok Koyi
  • 5,327
  • 8
  • 41
  • 50

2 Answers2

3

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();
    }

  }

}
Andy Wilkinson
  • 108,729
  • 24
  • 257
  • 242
0

Andy above answered the "how question", i.e how to use the library. But in this answer I've attempted to answer the why question

Here is my understanding

  1. Spring reads the autoconfiguration file metadata from /META-INF/spring-autoconfigure-metadata.properties from classpath (one such file is present in spring-boot-autoconfigure)

    • This file is generated by a plugin based on the @Configuration classes listed under org.springframework.boot.autoconfigure.EnableAutoConfiguration key within /META-INF/spring.factories file by spring-boot-autoconfigure-processor plugin
    • The generated file contains @Configuration class level annotations such as @ConditionalOnClass, @AutoConfigureBefore, e.t.c]
  2. Spring builds metadata about each of @Configuration classes retrieved from the above properties file
  3. Spring then evaluates all @Configuration class level conditions including @ConditionalOnClass against current classpath & bean factory. If conditions succeed, it loads the @Configuration class using standard java class loading
  4. If we keep @ConditionalOnClass(MissingClass.class) at @Configuration level, spring simply does not load the class load the configuration class if the class level conditions does not evaluate to true, say if the class is not there in classpath
  5. But if we keep @ConditionalOnClass(MissingClass.class) at @Bean level, spring will attempt to load our @Configuration class (assuming all conditions at @Configuration class level are satisfied) & we will get NoClassDefFoundError during class load

Overall, we need to follow @Andy solution because of this reason

@Andy

Appreciate if you can give your view on whether this is how Spring internally implemented the @ConditionalOnClass feature

Thanks

Ashok Koyi
  • 5,327
  • 8
  • 41
  • 50