Search code examples
javaspringspring-bootspring-autoconfiguration

Evaluation of @ConditionalOn* in Spring Boot


Context

I created a minimalist Spring Boot project with only spring-boot-starter-web as a dependency. On my classpath I have spring-boot-autoconfigure JAR, that has preconfigured many different technologies, e.g. CassandraAutoConfiguration. Obviously, in the runtime, this autoconfiguration won't spin up any beans since the conditions won't match (because I don't have anything related to Cassandra on my classpath.

Question

How does Spring Boot handle the following?

  1. Packaging the JAR: How does Spring Boot package spring-boot-autoconfigure JAR so that it doesn't fail during compilation even though the dependencies for things like CassandraAutoConfiguration are missing? My assumption is that Spring Boot includes all the necessary dependencies during compilation but simply doesn't package them into the final JAR.
  2. Loading CassandraAutoConfiguration at runtime: How does Spring Boot manage to load CassandraAutoConfiguration even when many of the classes it references are missing on the classpath? Why doesn't this result in runtime failures? Errors shown in IDE because of missing classes on the classpath

Solution

  • I found the answer in @ConditionalOnClass java docs:

    /**
     * {@link Conditional @Conditional} that only matches when the specified classes are on
     * the classpath.
     * <p>
     * A {@code Class} {@link #value() value} can be safely specified on
     * {@code @Configuration} classes as the annotation metadata is parsed by using ASM before
     * the class is loaded. If a class reference cannot be used then a {@link #name() name}
     * {@code String} attribute can be used.
     * <p>
    ...
    */
    public @interface ConditionalOnClass {
    
        /**
         * The classes that must be present. Since this annotation is parsed by loading class
         * bytecode, it is safe to specify classes here that may ultimately not be on the
         * classpath, only if this annotation is directly on the affected component and
         * <b>not</b> if this annotation is used as a composed, meta-annotation. In order to
         * use this annotation as a meta-annotation, only use the {@link #name} attribute.
         * @return the classes that must be present
         */
        Class<?>[] value() default {};
    
        ...
    }
    
    

    So, as far as I understand, the reason the evaluation of @ConditionalOnClass doesn't fail in runtime is because, Spring doesn't use reflection. Instead, it reads files bytecode.