Spring Boot自动配置原理及完整流程
作者:程序员鸭梨
引言
Spring Boot的最大特点之一是"约定优于配置",通过自动配置(Auto Configuration)机制,开发者无需显式配置大量Bean,框架会根据classpath中的依赖、配置文件中的属性自动推断并注册所需的Bean。理解自动配置原理对于深入掌握Spring Boot、排查配置问题、开发自定义starter都至关重要。本文将深入剖析Spring Boot自动配置的完整流程、核心注解和工作机制。
一、自动配置入口
1.1 @SpringBootApplication注解
@SpringBootApplication是Spring Boot应用的入口注解,它是一个组合注解,包含三个核心注解。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@EnableConfigurationProperties
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes =
TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes =
AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication {
@AliasFor("scanBasePackageClasses")
Class<?>[] scanBasePackages() default {};
@AliasFor("scanBasePackages")
Class<?>[] scanBasePackageClasses() default {};
@AliasFor("exclude")
Class<?>[] excludeClasses() default {};
Class<?>[] exclude() default {};
}
1.2 @EnableAutoConfiguration
@EnableAutoConfiguration是自动配置的核心启用注解,它通过@Import导入AutoConfigurationImportSelector。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY =
"spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
二、自动配置选择器
2.1 AutoConfigurationImportSelector
AutoConfigurationImportSelector实现了DeferredImportSelector接口,负责选择需要导入的自动配置类。
public class AutoConfigurationImportSelector
extends AutoConfigurationGroup
implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata
annotationMetadata) {
List<String> configurations =
getCandidateConfigurations(annotationMetadata,
new AnnotationAttributes(
getAttributes(annotationMetadata)));
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(
annotationMetadata,
getAttributes(annotationMetadata));
configurations.removeAll(exclusions);
configurations = sort(configurations);
return configurations.toArray(new String[0]);
}
}
2.2 自动配置加载流程
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. If you are using a custom " +
"packaging, make sure that file is correct.");
return configurations;
}
三、spring.factories文件
3.1 文件位置和格式
自动配置类通过META-INF/spring.factories文件声明。
# 文件位置: META-INF/spring.factories # org.springframework.boot.autoconfigure.EnableAutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
3.2 Spring Boot 2.7+变化
Spring Boot 2.7引入了新的自动配置文件位置META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports。
# 文件位置: META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports # 每行一个配置类 com.example.autoconfig.CustomAutoConfiguration
// Spring Boot 2.7+ 的加载方式
private List<String> getImports(String name) {
AutoConfigurationEntry entry =
((AutoConfigurationMetadata) ClassUtils
.forName(AutoConfigurationMetadata.class.getName(),
getClass().getClassLoader())
.loadMetadata(name));
return entry.getConfigurations();
}
四、条件注解
4.1 @Conditional系列注解
条件注解是自动配置的核心机制,只有满足特定条件时,对应的配置类才会生效。
// 条件注解继承关系 @Conditional(OnClassCondition.class) // 类路径中存在指定类 @Conditional(OnBeanCondition.class) // 容器中存在指定Bean @Conditional(OnPropertyCondition.class) // 配置属性满足条件 @Conditional(OnWebApplicationCondition.class) // 是Web应用 @Conditional(OnExpressionCondition.class) // SpEL表达式为true @Conditional(OnNotTomcatCondition.class) // 不是Tomcat容器
4.2 条件注解使用示例
@Configuration
@ConditionalOnClass(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean(DataSource.class)
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
@Bean
@ConditionalOnClass(JdbcTemplate.class)
@ConditionalOnMissingBean(JdbcTemplate.class)
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
@Configuration
@ConditionalOnProperty(
prefix = "spring.datasource",
name = "url",
havingValue = "oracle"
)
public class OracleDataSourceConfiguration {
// 仅当配置了Oracle数据源时才生效
}
五、自动配置流程详解
5.1 完整流程
Spring Boot自动配置的完整流程如下:应用启动时,SpringFactoriesLoader加载spring.factories;解析@EnableAutoConfiguration获取自动配置类列表;通过@Conditional注解过滤不满足条件的配置;按@AutoConfigureAfter指定的顺序加载配置;注册过滤后的Bean定义。
// SpringApplication.run() 核心流程简化
public ConfigurableApplicationContext run(String... args) {
// 创建SpringApplication
SpringApplication application = new SpringApplication(
PrimarySource.class);
// 获取并运行自动配置
application.setAllowBeanDefinitionOverriding(true);
ConfigurableApplicationContext context =
application.run(args);
return context;
}
5.2 配置属性绑定
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties {
private String url;
private String username;
private String password;
private ClassLoader classLoader;
public DataSource initializeDataSourceBuilder() {
return DataSourceBuilder.create()
.getEffectiveProperties();
}
public DataSource createDataSource() {
return initializeDataSourceBuilder().build();
}
}
六、自定义自动配置
6.1 创建自定义starter
// 1. 创建配置Properties
@ConfigurationProperties(prefix = "app.cache")
public class AppCacheProperties {
private String type = "caffeine";
private long maximumSize = 1000;
private long expireAfterWriteSeconds = 3600;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
// getters and setters
}
// 2. 创建自动配置类
@Configuration
@ConditionalOnClass(CacheManager.class)
@EnableConfigurationProperties(AppCacheProperties.class)
public class AppCacheAutoConfiguration {
private final AppCacheProperties properties;
public AppCacheAutoConfiguration(
AppCacheProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean(CacheManager.class)
public CacheManager cacheManager() {
if ("caffeine".equals(properties.getType())) {
return createCaffeineCacheManager();
} else if ("redis".equals(properties.getType())) {
return createRedisCacheManager();
}
return new ConcurrentMapCacheManager();
}
private CacheManager createCaffeineCacheManager() {
CaffeineCacheManager manager =
new CaffeineCacheManager();
manager.setMaximumSize(properties.getMaximumSize());
manager.setExpireAfterWrite(
Duration.ofSeconds(
properties.getExpireAfterWriteSeconds()));
return manager;
}
private CacheManager createRedisCacheManager() {
RedisCacheManager manager = new RedisCacheManager(
redisConnectionFactory);
manager.setDefaultExpiration(
properties.getExpireAfterWriteSeconds());
return manager;
}
}
// 3. 创建spring.factories
// META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.AppCacheAutoConfiguration
6.2 条件化自定义配置
@Configuration
@ConditionalOnProperty(
prefix = "app.feature",
name = "cache.enabled",
havingValue = "true",
matchIfMissing = true
)
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@EnableConfigurationProperties(AppCacheProperties.class)
public class RedisCacheAutoConfiguration {
private final AppCacheProperties properties;
private final RedisConnectionFactory connectionFactory;
public RedisCacheAutoConfiguration(
AppCacheProperties properties,
RedisConnectionFactory connectionFactory) {
this.properties = properties;
this.connectionFactory = connectionFactory;
}
@Bean
public CacheManager redisCacheManager() {
RedisCacheConfiguration config =
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofSeconds(
properties.getExpireAfterWriteSeconds()));
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}
七、配置属性提示
7.1 元数据生成
Spring Boot自动配置支持IDE属性提示,需要配置spring-configuration-metadata.json。
// src/main/resources/META-INF/spring/spring-configuration-metadata.json
{
"groups": [
{
"name": "app.cache",
"type": "com.example.config.AppCacheProperties",
"description": "缓存配置"
}
],
"properties": [
{
"name": "app.cache.type",
"type": "java.lang.String",
"description": "缓存类型",
"defaultValue": "caffeine"
},
{
"name": "app.cache.maximum-size",
"type": "java.lang.Long",
"description": "最大缓存数量",
"defaultValue": 1000
},
{
"name": "app.cache.expire-after-write-seconds",
"type": "java.lang.Long",
"description": "写入后过期时间(秒)",
"defaultValue": 3600
}
],
"hints": [
{
"name": "app.cache.type",
"values": [
{
"value": "caffeine",
"description": "使用Caffeine本地缓存"
},
{
"value": "redis",
"description": "使用Redis分布式缓存"
}
]
}
]
}
八、排除自动配置
8.1 多种排除方式
// 方式1:注解exclude属性
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class Application {
}
// 方式2:excludeName属性
@SpringBootApplication(excludeName = {
"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"
})
public class Application {
}
// 方式3:配置文件排除
// application.yml
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
- org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
8.2 按需启用自动配置
# 完全禁用自动配置 spring.boot.enableautoconfiguration=false # 只启用特定自动配置 spring.boot.autoconfiguration.enabled=true spring.boot.autoconfigure.exclude=\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
九、最佳实践
9.1 自动配置开发建议
保持自动配置类简洁,只负责核心配置;正确使用@Conditional注解,避免冲突;使用@AutoConfigureAfter确保加载顺序;提供合理的默认值和配置属性;创建spring-configuration-metadata.json提供IDE提示。
9.2 调试自动配置
// 启用自动配置调试日志
logging.level.org.springframework.boot.autoconfigure=DEBUG
// 在启动类添加以下代码查看自动配置报告
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(Application.class);
app.setShowBanner(false);
// 添加完整的自动配置报告
new SpringApplicationBuilder(Application.class)
.lazyInitialization(true)
.build()
.run(args);
}
}
总结
Spring Boot自动配置是框架智能化的核心体现,通过@EnableAutoConfiguration、@Conditional注解和spring.factories机制,框架能够根据classpath依赖和配置文件自动推断并注册所需Bean。理解这一机制不仅有助于排查配置问题,更能帮助开发者构建高质量的自定义starter。自动配置遵循"约定优于配置"的原则,在保持灵活性的同时大大简化了Spring应用的开发。
