Spring Boot - How auto configuration works?

[Updated: Jan 3, 2017, Created: Jan 1, 2017]

In this tutorial we are going to look at the logic behind Spring boot auto configuration.

Previously, we saw the usage of @EnableAutoConfiguration and how @SpringBootApplication annotation can be used as a shortcut of @EnableAutoConfiguration, @ComponentScan and @Configuration.

@EnableAutoConfiguration plays an import role in auto-configuration logic. Let's look at this annotation definition:

package org.springframework.boot.autoconfigure;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(EnableAutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

   Class<?>[] exclude() default {};
   String[] excludeName() default {};
}

There are two meta-annotation which we need to understand : @AutoConfigurationPackage and @Import(EnableAutoConfigurationImportSelector.class)


@AutoConfigurationPackage

package org.springframework.boot.autoconfigure;

import ....

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {

}

This annotation in turn uses another @Import with value= AutoConfigurationPackages.Registrar.class.

AutoConfigurationPackages.Registrar implements ImportBeanDefinitionRegistrar.

As covered in Spring core tutorials, the @Import annotation can accept following type of classes:


AutoConfigurationPackages.Registrar registers a bean which stores client side configuration package list for reference later. Spring boot uses this list internally, e.g. in data access configuration classes of spring-boot-autoconfigure. This list can be accessed via static method AutoConfigurationPackages.get(BeanFactory):

package com.logicbig.example;

import ...

@EnableAutoConfiguration
public class AutoConfigurationPackagesTest {

   public static void main (String[] args) {

       SpringApplication app =
                     new SpringApplication(AutoConfigurationPackagesTest.class);
       app.setBannerMode(Banner.Mode.OFF);
       app.setLogStartupInfo(false);
       ConfigurableApplicationContext c = app.run(args);
       List<String> packages = AutoConfigurationPackages.get(c);
       System.out.println("packages: "+packages);
   }
}

Output

 2017-01-03 10:17:37.372  INFO 10752 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@67b467e9: startup date [Tue Jan 03 10:17:37 CST 2017]; root of context hierarchy
2017-01-03 10:17:38.155  INFO 10752 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
packages: [com.logicbig.example]
2017-01-03 10:17:38.170  INFO 10752 --- [       Thread-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@67b467e9: startup date [Tue Jan 03 10:17:37 CST 2017]; root of context hierarchy
2017-01-03 10:17:38.171  INFO 10752 --- [       Thread-1] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown


@Import(EnableAutoConfigurationImportSelector.class)

This annotation is responsible for boot auto-configuration mechanism.

EnableAutoConfigurationImportSelector implements DeferredImportSelector. This selector implementation uses spring core SpringFactoriesLoader.loadFactoryNames() which loads configuration classes from META-INF/spring.factories.

The boot configuration classes are loaded from spring-boot-autoconfigure-1.4.2.RELEASE.jar!/META-INF/spring.factories (in the file, search for the key org.springframework.boot.autoconfigure.EnableAutoConfiguration).

Note that spring-boot-autoconfigure module is implicitly included in all boot application.

Let's have a look at one of such configuration classes loaded from spring.factories. Following is a snippet from JmxAutoConfiguration:

 package org.springframework.boot.autoconfigure.jmx;

   .......
 import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
 import org.springframework.boot.autoconfigure.condition.SearchStrategy;
  .....

 @Configuration
 @ConditionalOnClass({ MBeanExporter.class })
 @ConditionalOnProperty(prefix = "spring.jmx", name = "enabled", havingValue = "true", matchIfMissing = true)
 public class JmxAutoConfiguration implements
                                    EnvironmentAware, BeanFactoryAware {
    .....
 }


What is @ConditionalOnClass?

@ConditionalOnClass is itself meta-annotated with @Conditional(OnClassCondition.class). As we know that the configuration classes/methods annotated with @Conditional will be registered with bean factory only if the corresponding Condition#matches returns true (Check out our @Conditional tutorial).

OnClassCondition#matches() will return true if the specified class (MBeanExporter.class, in above example) is in the classpath. This check is done by loading the class (similar to Class.forName()). If that fails then it's assumed that the class is not in classpath.


A boot auto-configuration class (such as JmxAutoConfiguration.java in above case) usually has bunch of methods annotated with @Bean (Of course it's a configuration class). Generally speaking, based on whether a specific target class is in classpath, other beans are auto-configured by such configuration class. For desired auto-configuration to work, we must add related starter dependency. So that the target class can be found in the classpath.



What is @ConditionalOnProperty?

The annotation @ConditionalOnProperty (used in above example) is another @Conditional variant. it is annotated with @Conditional(OnPropertyCondition.class).

OnPropertyCondition#matches() will return true if client application has specified the target property with the specified value. Please check out our last tutorial to see how properties can be specified on the client side.

Consider above example snippet:

  @ConditionalOnProperty(prefix = "spring.jmx", name = "enabled",
                         havingValue = "true", matchIfMissing = true)
  

This condition will be true if we provide spring.jmx.enabled=true.

'matchIfMissing=true' means by default (if property is not set) this condition will be true.

For above example class to be included in application configuration, both conditions @ConditionalOnClass and @ConditionalOnProperty should be evaluated to be true.


Other Conditional annotations

Other similar conditional annotations can be found in the package org.springframework.boot.autoconfigure.condition. All these annotation follow ConditionalOnXyz naming convention. For the developers who are going to create their own starter should explore all this API. For other developer it's good enough to know the high level mechanism how things work.




Using --debug

To investigate what auto-configuration is currently being applied to our application, we will start our application with the Java arg --debug or with JVM option -Ddebug. Let's see that with an example. We are going to include spring-boot-starter dependency in this example:

@EnableAutoConfiguration
public class DebugModeExample {

  public static void main (String[] args) {
      //just doing this programmatically for demo
      String[] appArgs = {"--debug"};

      SpringApplication app = new SpringApplication(DebugModeExample.class);
      app.setBannerMode(Banner.Mode.OFF);
      app.setLogStartupInfo(false);
      app.run(appArgs);
    }
}

Output

2017-01-02 21:15:17.322 DEBUG 5704 --- [           main] o.s.boot.SpringApplication               : Loading source class com.logicbig.example.DebugModeExample
2017-01-02 21:15:17.379 DEBUG 5704 --- [           main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped (empty) config file 'file:/D:/LogicBig/example-projects/spring-boot/boot-customizing-autoconfig/target/classes/application.properties' (classpath:/application.properties)
2017-01-02 21:15:17.379 DEBUG 5704 --- [           main] o.s.b.c.c.ConfigFileApplicationListener  : Skipped (empty) config file 'file:/D:/LogicBig/example-projects/spring-boot/boot-customizing-autoconfig/target/classes/application.properties' (classpath:/application.properties) for profile default
2017-01-02 21:15:17.384  INFO 5704 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@2f0a87b3: startup date [Mon Jan 02 21:15:17 CST 2017]; root of context hierarchy
2017-01-02 21:15:18.032  INFO 5704 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2017-01-02 21:15:18.047 DEBUG 5704 --- [           main] utoConfigurationReportLoggingInitializer :


=========================
AUTO-CONFIGURATION REPORT
=========================


Positive matches:
-----------------

   GenericCacheConfiguration matched:
      - Cache org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration automatic cache type (CacheCondition)

   JmxAutoConfiguration matched:
      - @ConditionalOnClass found required class 'org.springframework.jmx.export.MBeanExporter' (OnClassCondition)
      - @ConditionalOnProperty (spring.jmx.enabled=true) matched (OnPropertyCondition)

   JmxAutoConfiguration#mbeanExporter matched:
      - @ConditionalOnMissingBean (types: org.springframework.jmx.export.MBeanExporter; SearchStrategy: current) did not find any beans (OnBeanCondition)

   JmxAutoConfiguration#mbeanServer matched:
      - @ConditionalOnMissingBean (types: javax.management.MBeanServer; SearchStrategy: all) did not find any beans (OnBeanCondition)

   JmxAutoConfiguration#objectNamingStrategy matched:
      - @ConditionalOnMissingBean (types: org.springframework.jmx.export.naming.ObjectNamingStrategy; SearchStrategy: current) did not find any beans (OnBeanCondition)

   NoOpCacheConfiguration matched:
      - Cache org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration automatic cache type (CacheCondition)

   PropertyPlaceholderAutoConfiguration#propertySourcesPlaceholderConfigurer matched:
      - @ConditionalOnMissingBean (types: org.springframework.context.support.PropertySourcesPlaceholderConfigurer; SearchStrategy: current) did not find any beans (OnBeanCondition)

   RedisCacheConfiguration matched:
      - Cache org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration automatic cache type (CacheCondition)

   SimpleCacheConfiguration matched:
      - Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type (CacheCondition)


Negative matches:
-----------------

   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)

   AopAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice' (OnClassCondition)

   ArtemisAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.artemis.jms.client

 ...............................
 ....................


Exclusions:
-----------

    None


Unconditional classes:
----------------------

    org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration

    org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration

    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration

    org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration



2017-01-02 21:15:18.058  INFO 5704 --- [       Thread-1] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@2f0a87b3: startup date [Mon Jan 02 21:15:17 CST 2017]; root of context hierarchy
2017-01-02 21:15:18.059  INFO 5704 --- [       Thread-1] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

In above AUTO-CONFIGURATION REPORT:

  • Positive matches: where @Conditional evaluated to true and the configuration classes have been included.
  • Negative matches: where @Conditional evaluated to false and the configuration classes have not been included.
  • Exclusions: the configuration classes which we explicitly excluded on application side. We will see what's that in the next section.
  • Unconditional classes: where auto-configuration classes that do not have any class-level conditions, i.e. the class will be always be part of the application’s configuration.



Disabling specific auto-configuration

If we don't want some of the auto-configuration applied to our application, we can use the exclude attribute of @EnableAutoConfiguration to disable them.

Let's disable JmxAutoConfiguration (used in the last example).

@EnableAutoConfiguration(exclude = {JmxAutoConfiguration.class})
public class ExcludeConfigExample {

    public static void main (String[] args) {
         //just doing this programmatically for demo
         String[] appArgs = {"--debug"};

        SpringApplication app = new SpringApplication(ExcludeConfigExample.class);
        app.setBannerMode(Banner.Mode.OFF);
        app.setLogStartupInfo(false);
        app.run(appArgs);
    }
}

Output


  .............

=========================
AUTO-CONFIGURATION REPORT
=========================


Positive matches:
-----------------

   GenericCacheConfiguration matched:
      - Cache org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration automatic cache type (CacheCondition)

   NoOpCacheConfiguration matched:
      - Cache org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration automatic cache type (CacheCondition)

   PropertyPlaceholderAutoConfiguration#propertySourcesPlaceholderConfigurer matched:
      - @ConditionalOnMissingBean (types: org.springframework.context.support.PropertySourcesPlaceholderConfigurer; SearchStrategy: current) did not find any beans (OnBeanCondition)

   RedisCacheConfiguration matched:
      - Cache org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration automatic cache type (CacheCondition)

   SimpleCacheConfiguration matched:
      - Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type (CacheCondition)


Negative matches:
-----------------

   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)

   AopAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required classes 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice' (OnClassCondition)


  .................................

Exclusions:
-----------

    org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration


Unconditional classes:
----------------------

    org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration

    org.springframework.boot.autoconfigure.web.WebClientAutoConfiguration

    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration

    org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration



Note that you can also exclude auto-configuration classes via the spring.autoconfigure.exclude property.




Example Project


Dependencies and Technologies Used :
  • Spring Boot Starter 1.4.2.RELEASE: Core starter, including auto-configuration support, logging and YAML.
    Corresponding Spring version: 4.3.4.RELEASE
  • JDK 1.8
  • Maven 3.3.9

Customizing Auto Config Select All Download
  • boot-customizing-autoconfig
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources