Unveiling Spring Boot Auto‑Configuration: How It Works Under the Hood

This article walks through the inner workings of Spring Boot’s auto‑configuration mechanism, starting from a simple Hello World project, dissecting key annotations like @SpringBootApplication and @EnableAutoConfiguration, and tracing the source code of selectors, import groups, and the SpringApplication run process to reveal how beans are discovered, loaded, and registered at startup.

Tuhu Marketing Technology Team
Tuhu Marketing Technology Team
Tuhu Marketing Technology Team
Unveiling Spring Boot Auto‑Configuration: How It Works Under the Hood

Introduction

The first part of the Spring Cloud source‑code series introduces Spring Boot’s auto‑configuration principle, explaining that understanding this mechanism helps grasp Spring Cloud internals. Spring Boot is a rapid‑development framework built on Spring, bundling common dependencies and launching as an executable JAR.

Hello World Example

A minimal Spring Boot project requires adding the Spring Boot starter parent to the Maven POM, annotating the main class with @SpringBootApplication, and providing an application.yml (or application.properties) file.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.0.RELEASE</version>
</parent>
@SpringBootApplication
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
spring:
  application:
    name: eureka-server
server:
  port: 9000

Running the project prints the Spring Boot start banner, confirming successful startup.

Auto‑Configuration Source Analysis

When additional third‑party components (e.g., RabbitMQ, Redis) are added via application.yml, Spring Boot injects beans such as RabbitTemplate and RedisTemplate. The article explores how these beans are discovered through the auto‑configuration source code.

Key Annotations

The entry point is @SpringBootApplication, which itself combines three annotations: @SpringBootConfiguration: marks the class as a Spring Boot configuration. @EnableAutoConfiguration: triggers the auto‑configuration process. @ComponentScan: scans the classpath for components.

EnableAutoConfiguration Mechanism

@EnableAutoConfiguration

imports AutoConfigurationImportSelector. This selector implements DeferredImportSelector, allowing Spring to sort and filter imports across multiple selectors.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    Class<?>[] exclude() default {};
    String[] excludeName() default {};
}

The selector loads all candidate configuration class names from META-INF/spring.factories using SpringFactoriesLoader.loadFactoryNames, which reads the factories file from the classpath.

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
        getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories...");
    return configurations;
}

Typical entries include

org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration

,

org.springframework.boot.autoconfigure.redis.RedisAutoConfiguration

, and many others.

Deferred Import Selector Handling

The selector is first collected as a DeferredImportSelectorHolder. After all configuration classes are parsed, the DeferredImportSelectorGroupingHandler processes the collected selectors, sorting them and invoking their getImportGroup() to obtain a group that performs the actual import.

public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
    DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
    if (this.deferredImportSelectors == null) {
        DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
        handler.register(holder);
        handler.processGroupImports();
    } else {
        this.deferredImportSelectors.add(holder);
    }
}

The group’s process method records each import, and selectImports finally returns the list of auto‑configuration classes to be registered.

public Iterable<Group.Entry> getImports() {
    for (DeferredImportSelectorHolder deferredImport : this.deferredImports) {
        this.group.process(deferredImport.getConfigurationClass().getMetadata(),
                         deferredImport.getImportSelector());
    }
    return this.group.selectImports();
}

SpringApplication Initialization

The bootstrap starts with creating a SpringApplication instance, which deduces the web application type, loads ApplicationContextInitializer and ApplicationListener implementations from spring.factories, and records the primary source class.

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    setInitializers(getSpringFactoriesInstances(ApplicationContextInitializer.class));
    setListeners(getSpringFactoriesInstances(ApplicationListener.class));
    this.mainApplicationClass = deduceMainApplicationClass();
}

Run Process Overview

The static run method delegates to a new SpringApplication instance and executes the following steps:

Create DefaultApplicationArguments.

Prepare the ConfigurableEnvironment.

Print the startup banner.

Create the ConfigurableApplicationContext.

Prepare the context (register initializers, listeners, and singleton beans).

Refresh the context, which triggers the ConfigurationClassPostProcessor to process auto‑configuration classes.

Invoke any ApplicationRunner or CommandLineRunner beans.

During the refresh, invokeBeanFactoryPostProcessors delegates to ConfigurationClassPostProcessor, which parses @Configuration classes, follows @Import declarations, and ultimately registers bean definitions discovered via @Bean methods.

private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(
        beanFactory, getBeanFactoryPostProcessors());
    // Additional handling for LoadTimeWeaver, etc.
}

Conclusion

Spring Boot’s auto‑configuration is driven by a chain of selectors and import groups that read spring.factories, resolve deferred imports, and register @Bean definitions into the application context. Understanding this flow demystifies how third‑party components become available without explicit configuration.

Spring Boot auto‑configuration diagram
Spring Boot auto‑configuration diagram
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaSpring Bootdependency-injectionauto-configurationSpring Framework
Tuhu Marketing Technology Team
Written by

Tuhu Marketing Technology Team

Official tech channel of the Tuhu Marketing Technology Team, offering a developer community. We share the challenges, design concepts, and solutions from building our systems, aimed at internet developers. Follow us!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.