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.
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: 9000Running 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
@EnableAutoConfigurationimports 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
