Skip to content

Spring Boot Auto Configuration

Understand how Spring Boot auto-configuration works under the hood with practical examples of embedded Tomcat configuration and conditional beans.

Spring Boot has gained lots of popularity since it was first released by Pivotal (now part of VMware). As described on their website, it makes it easy to create stand-alone, production-grade Spring-based applications that you can “just run”. It also tries to be opinionated to reduce configuration boilerplate. But, have you wondered how does Spring Boot achieve that? How does it configure your application automagically?

In this post, I’ll try to explain the auto-configuration mechanism focusing on the embedded web server part, which is one of the most interesting ones. By going through this example, you’ll understand how the autoconfiguration works and which conditions are used to include (or not) a specific feature in your application.

Spring Initializr showing autoconfiguration options
Spring Initializr with different starter dependencies that trigger auto-configuration

How Spring Boot Autoconfiguration Works

The auto-configuration logic in Spring Boot is implemented by using some @Conditional* Spring annotations, introduced in Spring 4. These annotations allow Spring to include (or not) beans in the Application Context depending on the presence of other beans, classes in the classpath, configuration properties, etc.

By combining these conditions with the JARs present in your classpath, Spring Boot can make a pretty good guess about which functionality your application needs and configure the Application Context accordingly.

Let’s see how this works by explaining how the embedded Tomcat server gets configured.

The Embedded Tomcat Configuration

When you include spring-boot-starter-web in your project, Spring Boot will automatically configure an embedded Tomcat server for you. But how does this magic happen?

Let’s follow the dependency chain:

1. Starter Dependencies

When you add spring-boot-starter-web to your project, it brings in several transitive dependencies:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

This starter includes:

  • spring-boot-starter-tomcat (embedded Tomcat)
  • spring-webmvc (Spring MVC)
  • Other web-related dependencies

2. Auto-Configuration Classes

The @EnableAutoConfiguration annotation (included in @SpringBootApplication) triggers Spring Boot to look for auto-configuration classes in the classpath.

One of these classes is ServletWebServerFactoryAutoConfiguration:

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
         ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
         ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
         ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    // Configuration logic here
}

3. Conditional Configuration

Notice the @ConditionalOnClass(ServletRequest.class) annotation. This means this configuration will only be activated if ServletRequest.class is present in the classpath.

The @Import annotation brings in specific configurations for different embedded servers. Let’s look at the Tomcat one:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, 
                         search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {

    @Bean
    public TomcatServletWebServerFactory tomcatServletWebServerFactory(
            ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
            ObjectProvider<TomcatContextCustomizer> contextCustomizers,
            ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        factory.getTomcatConnectorCustomizers()
               .addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatContextCustomizers()
               .addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
        factory.getTomcatProtocolHandlerCustomizers()
               .addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
        return factory;
    }
}

4. The Conditions Explained

The EmbeddedTomcat configuration has several conditions:

  • @ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class }): This configuration is only activated if all these classes are present in the classpath.
  • @ConditionalOnMissingBean(value = ServletWebServerFactory.class): This bean is only created if there isn’t already a ServletWebServerFactory bean in the application context.

5. Dependency Chain

The presence of these classes in the classpath comes from the dependency chain:

spring-boot-starter-web
└── spring-boot-starter-tomcat
    └── tomcat-embed-core
        └── Contains Tomcat.class, UpgradeProtocol.class, etc.

Testing the Auto-Configuration

You can see which auto-configurations are being applied to your application by adding this to your application.properties:

logging.level.org.springframework.boot.autoconfigure=DEBUG
debug=true

When you run your application, you’ll see output like:

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

Positive matches:
-----------------
ServletWebServerFactoryAutoConfiguration matched:
  - @ConditionalOnClass found required class 'javax.servlet.ServletRequest'
  - found 'session' scope
  
ServletWebServerFactoryConfiguration.EmbeddedTomcat matched:
  - @ConditionalOnClass found required classes 'javax.servlet.Servlet', 
    'org.apache.catalina.startup.Tomcat', 'org.apache.coyote.UpgradeProtocol'
  - @ConditionalOnMissingBean (types: org.springframework.boot.web.servlet.server.ServletWebServerFactory; 
    SearchStrategy: CURRENT) did not find any beans

Negative matches:
-----------------
ServletWebServerFactoryConfiguration.EmbeddedJetty:
  Did not match:
    - @ConditionalOnClass did not find required class 'org.eclipse.jetty.server.Server'

ServletWebServerFactoryConfiguration.EmbeddedUndertow:
  Did not match:
    - @ConditionalOnClass did not find required class 'io.undertow.Undertow'

Common Auto-Configuration Scenarios

Switching Embedded Servers

If you want to use Jetty instead of Tomcat, you can:

  1. Exclude Tomcat from the starter:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
  1. Add Jetty starter:
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

Now the @ConditionalOnClass conditions will match Jetty instead of Tomcat, and Spring Boot will configure Jetty automatically.

Custom Server Configuration

You can also provide your own ServletWebServerFactory bean to customize the server configuration:

@Bean
public ServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    factory.setPort(9000);
    factory.setSessionTimeout(10, TimeUnit.MINUTES);
    return factory;
}

Since there’s now a ServletWebServerFactory bean in the context, the @ConditionalOnMissingBean condition in the auto-configuration will prevent the default one from being created.

Key Auto-Configuration Annotations

Here are the most common conditional annotations used in Spring Boot auto-configuration:

  • @ConditionalOnClass: Configuration is included only if specified classes are present in classpath
  • @ConditionalOnMissingClass: Configuration is included only if specified classes are NOT present
  • @ConditionalOnBean: Configuration is included only if specified bean is present in context
  • @ConditionalOnMissingBean: Configuration is included only if specified bean is NOT present in context
  • @ConditionalOnProperty: Configuration is included based on presence/value of configuration properties
  • @ConditionalOnWebApplication: Configuration is included only in web applications
  • @ConditionalOnNotWebApplication: Configuration is included only in non-web applications

Creating Custom Auto-Configuration

You can create your own auto-configuration by:

  1. Creating a configuration class with conditional annotations
  2. Adding it to META-INF/spring.factories file in your JAR
  3. Including the JAR in your project’s classpath

Example spring.factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyCustomAutoConfiguration

Understanding the Magic

The “magic” of Spring Boot auto-configuration boils down to:

  1. Opinionated Defaults: Spring Boot makes reasonable assumptions about what you need based on your dependencies
  2. Conditional Configuration: Using @Conditional* annotations to include/exclude configurations based on classpath and context
  3. Starter Dependencies: Carefully crafted dependency sets that bring in the right classes to trigger auto-configuration
  4. Override Capability: You can always override the defaults by providing your own beans

The embedded Tomcat example shows how all these pieces work together:

  • The spring-boot-starter-web brings in Tomcat classes
  • The presence of these classes triggers the Tomcat auto-configuration
  • If you provide your own server factory bean, the auto-configuration backs off

Conclusion

Spring Boot’s auto-configuration is not magic—it’s a clever use of Spring’s conditional configuration features combined with well-designed starter dependencies. By understanding how @ConditionalOnClass, @ConditionalOnMissingBean, and other conditional annotations work, you can better understand what Spring Boot is doing for you and how to customize it when needed.

The embedded server example demonstrates the key principles:

  • Dependencies bring in classes that trigger auto-configuration
  • Conditional annotations determine when configurations should be applied
  • You can always override defaults by providing your own beans
  • The debug output helps you understand which configurations are being applied

This understanding will help you debug issues, customize configurations, and even create your own auto-configurations when building reusable libraries or internal frameworks.

Master Microservices with Spring Boot

A hands-on guide to building production-ready microservices

Get the Book

Get More Architecture Insights

Join hundreds of software architects and engineers getting practical insights delivered to their inbox.

Back to Blog