How Spring Boot auto-configuration works

As part of Spring Boot tutorial, in this post, we will see how Spring Boot auto-configuration works. We will have a greater look at the auto-configurations internals.

 

Introduction

Spring Boot auto-configuration automatically configure a Spring application based on the dependencies present on the classpath. Spring Boot detects classes in the classpath and auto-configuration mechanism will ensure to create and wires necessary beans for us. This is one of the most powerful feature of the Spring Boot and most of the work happens silently in the background.

 

1. Auto-Configuration Report

Before we get in to the details, let’s enable auto-configuration report for our Spring Boot application. You can create a simple Spring Boot application using initializr. Set the debug=true in the application.properties file. Once you run the application, you may notice similar output in the console:

============================
CONDITIONS EVALUATION REPORT
============================

Positive matches:
-----------------
   CodecsAutoConfiguration matched:
      - @ConditionalOnClass found required class 'org.springframework.http.codec.CodecConfigurer' (OnClassCondition)

   ErrorMvcAutoConfiguration.WhitelabelErrorViewConfiguration#defaultErrorView matched:
      - @ConditionalOnMissingBean (names: error; SearchStrategy: all) did not find any beans (OnBeanCondition)

   JacksonHttpMessageConvertersConfiguration.MappingJackson2HttpMessageConverterConfiguration matched:
      - @ConditionalOnClass found required class 'com.fasterxml.jackson.databind.ObjectMapper' (OnClassCondition)
      - @ConditionalOnProperty (spring.http.converters.preferred-json-mapper=jackson) matched (OnPropertyCondition)
      - @ConditionalOnBean (types: com.fasterxml.jackson.databind.ObjectMapper; SearchStrategy: all) found bean 'jacksonObjectMapper' (OnBeanCondition)

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

Negative matches:
-----------------
   ActiveMQAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'javax.jms.ConnectionFactory' (OnClassCondition)

   AopAutoConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'org.aspectj.lang.annotation.Aspect' (OnClassCondition)
         
...............................................................................................................................................................................
Exclusions:
-----------
   None

Unconditional classes:
----------------------
    org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration
    org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration

As clear in the above output, Spring Boot provides details about what all configuration are matching and what are excludes (which does not match) and what was the reason for this action. Based on these matches, spring boot auto-configuration will start, load and wire the configurations.

Let’s take the example of the JacksonHttpMessageConvertersConfiguration in the “positive match” section. Spring Boot finds the ObjectMapper in the class path (I am using a web application for this example), based on this match spring boot auto-configuration will load the Jackson configuration for our application.

 

2. Understanding @Conditional Annotation

Before we get into details of how Spring Boot auto-configuration works, let’s take a broader look at the @Conditional annotation. This annotation is the base and most annotations used by the Spring Boot auto-configuration are extensions of this annotation. The @Conditional annotation introduced in Spring 4 is an improvement to the Spring profile feature. It adds flexibility to the developer to register beans based on several conditions like:

  1. A type-level annotation on any class directly or indirectly annotated with @Component, including @Configuration classes.
  2. As a meta-annotation, for the purpose of building custom stereotype annotations.
  3. Method-level annotation on any @Bean method.

Let’s pick an example, where we need to implement a tax rate service based on user country. Let’s examine how our code base looks like: 

public interface TaxCalculationService{
  Double getTaxRate();
}

This is our TaxCalculationService implementation detail

public class USTaxCalculationService implements TaxCalculationService{
    @Override
    public Double getTaxRate(){
      // Tax rate for US
    }
}

public class CATaxCalculationService implements TaxCalculationService{
    @Override
    public Double getTaxRate(){
      // Tax rate for Canada
    }
}

Let’s carry out the condition based on the user country:

public class USTaxCalculationTypeCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata metadata) {
        return Locale.getDefault().equals(Locale.US);
    }
}

We can have an identical implementation for the Canadian tax service. Let’s look at how to apply these conditions in our configurations to load beans conditionally:

@Configuration
public class AppConfig {

 @Bean 
 @Conditional(USTaxCalculationTypeCondition.class)
 public TaxCalculationService usTaxCalculationService() {
  return new USTaxCalculationService();
 }

 @Bean 
 @Conditional(CATaxCalculationTypeCondition.class)
 public TaxCalculationService caTaxCalculationService() {
  return new CATaxCalculationService();
 }
}

If we run our application, Spring will ensure to load the correct bean based on country (Locale in our example). Now we have the key knowledge of The @Conditional annotation,let’s learn how Spring Boot use this feature for the auto-configuration.

 

3. Understanding Auto-configuration

Internally auto-configuration is achieved with @Configuration annotation. Let’s look at the out of the box JacksonAutoConfiguration for a further understanding:

@Configuration
@ConditionalOnClass({ObjectMapper.class})
public class JacksonAutoConfiguration {
  // auto-configuration stuff
}

Spring Boot, use the @Conditional annotations to determine if it require setting up auto-configuration for the configuration or not. Usually auto-configuration classes use the following conditional annotations:

  1. @ConditionalOnClass
  2. @ConditionalOnMissingBean

Out of the box, Spring provides plenty of conditional annotations.Please consult reference documentation 

 

3.1 The @ConditionalOnClass Annotation

The class conditions are the most common conditional annotation use by auto-configuration for the detection. The @ConditionalOnClass annotation allows it to include the configuration class based on a specific class. Here is our JacksonAutoConfiguration configuration class:

@Configuration
@ConditionalOnClass({ObjectMapper.class})
public class JacksonAutoConfiguration {
  // auto-configuration stuff
}

In this example, Spring Boot auto-configuration includes JacksonAutoConfiguration, if it finds Object mapper in the classpath. We can likewise use the @ConditionalOnMissingClass annotation to enable configuration if the class is missing in the classpath.

 

3.2 The @ConditionalOnMissingBean Annotation

We use this annotation to verify the absence of a specific bean. Let’s look at the example for a better insight:

@Configuration
@ConditionalOnClass({ObjectMapper.class})
public class JacksonAutoConfiguration {
 
    // additional configuration code 
    @Bean
    @Primary
    @ConditionalOnMissingBean
    public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
        return builder.createXmlMapper(false).build();
    }
}

In above example, it will create the ObjectMapper bean if it already contains no bean of type ObjectMapper in the ApplicationContext. The other annotation @ConditionalOnBean works opposite to the @ConditionalOnMissingBean annotation.

 

4. Spring Boot Auto-Configuration

To enable the auto-configuration magic, Spring Boot uses the @EnableAutoConfiguration annotation. Normally we use the @SpringBootApplication annotation which includes our @EnableAutoConfiguration annotation. The @EnableAutoConfiguration annotation enables the auto-configuration of Spring ApplicationContext by scanning the classpath components and registers the beans that are matching various Conditions.

 

4.1 Locating Auto-configuration Classes

To load auto-configuration classes, Spring needs to know where to look for it. Spring checks  META-INF/spring.factories file within your published jar. This file should contain a list of the configuration class. Let’s look at the spring.factories file under the auto-configuration jar

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\

This is a standard component of the Spring Core and it uses SpringFactoriesLoader, get the list of Java Configurations classes configured using the org.springframework.boot.autoconfigure.EnableAutoConfiguration property key.

 

5. Custom Auto-Configuration

To get a further understanding, I firmly suggest reading  Custom Starter with Spring Boot

 

Summary

In this article, we saw how Spring Boot auto-configuration works. We examined how Spring Boot uses the @Conditional annotation and  SpringFactoriesLoader feature to implement auto-configuration.