Spring Boot 自动配置原理
Spring Boot 的核心思想是"约定优于配置",通过自动配置大幅减少了 Spring 应用的配置工作。理解自动配置原理,有助于我们更好地使用 Spring Boot,也能在遇到问题时快速定位和解决。
自动配置概述
什么是自动配置?
在传统的 Spring 应用中,我们需要手动配置大量的 Bean。例如,要使用数据源,需要配置 DataSource、JdbcTemplate、TransactionManager 等。对于一个简单的数据库应用,配置代码可能需要几十行。
Spring Boot 的自动配置机制会根据项目中的依赖、已定义的 Bean、配置属性等条件,自动创建和配置这些 Bean。我们只需要添加相应的依赖,Spring Boot 就会自动完成大部分配置工作。
自动配置的优势
减少配置工作量:大部分常用功能都有默认配置,开箱即用。
统一配置风格:通过 application.properties 或 application.yml 统一管理配置。
降低学习成本:不需要了解每个组件的详细配置方式。
灵活可定制:默认配置可以被覆盖,满足个性化需求。
核心原理
@SpringBootApplication 注解
每个 Spring Boot 应用都有一个主类,标注了 @SpringBootApplication 注解。这个注解是一个组合注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration // 标记为配置类
@EnableAutoConfiguration // 启用自动配置
@ComponentScan( // 组件扫描
excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
}
)
public @interface SpringBootApplication {
}
三个核心注解的作用:
@SpringBootConfiguration:实际上是 @Configuration 的特化版本,表示这是一个配置类。
@EnableAutoConfiguration:启用自动配置,是自动配置的核心。
@ComponentScan:启用组件扫描,默认扫描主类所在包及其子包。
@EnableAutoConfiguration 工作原理
@EnableAutoConfiguration 是自动配置的核心注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
关键在于 @Import(AutoConfigurationImportSelector.class),它导入了 AutoConfigurationImportSelector 类,这个类负责加载自动配置类。
自动配置类的加载机制
AutoConfigurationImportSelector 通过以下步骤加载自动配置类:
-
读取
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件(Spring Boot 2.7+) -
在旧版本中读取
META-INF/spring.factories文件中EnableAutoConfiguration对应的配置类 -
对配置类进行条件过滤,只加载满足条件的配置类
例如,spring-boot-autoconfigure 包中的配置文件:
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
...
条件注解
条件注解是自动配置的核心机制,用于判断是否应该创建某个 Bean 或执行某个配置。
条件注解类型
Spring Boot 提供了丰富的条件注解:
类条件注解
// 当类路径下存在指定类时生效
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
}
// 当类路径下不存在指定类时生效
@ConditionalOnMissingClass("com.example.UnwantedClass")
public class AlternativeConfiguration {
}
Bean 条件注解
// 当容器中存在指定 Bean 时生效
@ConditionalOnBean(DataSource.class)
public class JdbcTemplateAutoConfiguration {
}
// 当容器中不存在指定 Bean 时生效
@ConditionalOnMissingBean
@Bean
public DataSource dataSource() {
return new HikariDataSource();
}
属性条件注解
// 当配置属性存在且具有指定值时生效
@ConditionalOnProperty(
name = "spring.datasource.type",
havingValue = "com.zaxxer.hikari.HikariDataSource"
)
public class HikariDataSourceConfiguration {
}
// 当属性不存在时也生效
@ConditionalOnProperty(
name = "app.feature.enabled",
matchIfMissing = true
)
public class FeatureConfiguration {
}
资源条件注解
// 当类路径下存在指定资源时生效
@ConditionalOnResource(resources = "classpath:db.properties")
public class DatabaseConfiguration {
}
Web 应用条件注解
// 当应用是 Servlet Web 应用时生效
@ConditionalOnWebApplication(type = Type.SERVLET)
public class WebMvcAutoConfiguration {
}
// 当应用是 Reactive Web 应用时生效
@ConditionalOnWebApplication(type = Type.REACTIVE)
public class WebFluxAutoConfiguration {
}
SpEL 条件注解
// 使用 SpEL 表达式判断条件
@ConditionalOnExpression("#{systemProperties['os.name'].toLowerCase().contains('windows')}")
public class WindowsSpecificConfiguration {
}
条件注解的组合使用
多个条件注解可以组合使用,所有条件都满足时才会生效:
@Configuration
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.url", matchIfMissing = true)
public class EmbeddedDataSourceConfiguration {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
}
这个配置类只有在以下条件都满足时才会生效:
- 类路径下存在
DataSource和EmbeddedDatabaseType类 - 容器中还没有
DataSourceBean - 配置属性
spring.datasource.url不存在
自动配置类分析
以 DataSourceAutoConfiguration 为例,分析自动配置类的结构:
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@Conditional(EmbeddedDatabaseCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import(EmbeddedDataSourceConfiguration.class)
protected static class EmbeddedDatabaseConfiguration {
}
@Configuration(proxyBeanMethods = false)
@Conditional(PooledDataSourceCondition.class)
@ConditionalOnMissingBean({ DataSource.class, XADataSource.class })
@Import({ HikariConfiguration.class, Tomcat.class, Dbcp2.class,
OracleUcp.class, Generic.class, DataSourceJmxConfiguration.class })
protected static class PooledDataSourceConfiguration {
}
}
这个配置类的逻辑是:
- 只有当类路径下存在
DataSource类时才会加载 - 使用
@EnableConfigurationProperties绑定配置属性 - 根据条件选择嵌入式数据库或连接池数据源
配置属性绑定
自动配置类通常与配置属性类配合使用:
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties {
private String url;
private String username;
private String password;
private String driverClassName;
private String type;
// getters and setters
}
在 application.properties 中配置:
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
自定义自动配置
创建自定义自动配置类
我们可以创建自己的自动配置类,让其他项目自动获得配置:
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyServiceProperties.class)
public class MyServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MyService myService(MyServiceProperties properties) {
return new MyService(properties.getHost(), properties.getPort());
}
}
@ConfigurationProperties(prefix = "my.service")
public class MyServiceProperties {
private String host = "localhost";
private int port = 8080;
private boolean enabled = true;
// getters and setters
}
注册自动配置类
在 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中注册:
com.example.autoconfigure.MyServiceAutoConfiguration
在旧版本中使用 META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.MyServiceAutoConfiguration
创建自定义 Starter
将自动配置类打包成 Starter,方便其他项目使用。Starter 的典型结构:
my-spring-boot-starter/
├── src/main/java/
│ └── com/example/autoconfigure/
│ ├── MyServiceAutoConfiguration.java
│ ├── MyServiceProperties.java
│ └── MyService.java
├── src/main/resources/
│ └── META-INF/
│ └── spring/
│ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
└── pom.xml
调试自动配置
查看自动配置报告
在 application.properties 中开启调试模式:
debug=true
启动应用后,控制台会输出自动配置报告,包括:
Positive matches:条件满足,已应用的自动配置
Negative matches:条件不满足,未应用的自动配置
Exclusions:被排除的自动配置
Unconditional classes:无条件加载的自动配置
使用 Actuator 查看配置
添加 Actuator 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
访问 /actuator/conditions 端点查看自动配置详情。
排除自动配置
如果不需要某些自动配置,可以排除:
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
或在配置文件中排除:
spring.autoconfigure.exclude=\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration
自动配置最佳实践
合理使用条件注解
条件注解应该精确表达配置生效的条件。例如,数据源配置应该检查数据库驱动是否存在:
@Configuration
@ConditionalOnClass({ DataSource.class, Driver.class })
public class DataSourceAutoConfiguration {
}
提供合理的默认值
自动配置应该提供合理的默认值,让应用能够直接运行:
@ConfigurationProperties(prefix = "app.cache")
public class CacheProperties {
private boolean enabled = true;
private int maxSize = 1000;
private Duration expireAfter = Duration.ofMinutes(10);
// getters and setters
}
允许用户覆盖配置
使用 @ConditionalOnMissingBean 让用户可以覆盖自动配置:
@Bean
@ConditionalOnMissingBean
public MyService myService() {
return new DefaultMyService();
}
用户可以定义自己的 Bean 来覆盖默认配置:
@Bean
public MyService myService() {
return new CustomMyService();
}
添加配置元数据
创建 META-INF/spring-configuration-metadata.json 文件,为配置属性提供元数据:
{
"properties": [
{
"name": "my.service.host",
"type": "java.lang.String",
"description": "服务主机地址",
"defaultValue": "localhost"
},
{
"name": "my.service.port",
"type": "java.lang.Integer",
"description": "服务端口",
"defaultValue": 8080
}
]
}
这样在 IDE 中配置属性时会获得自动完成和提示功能。
常见自动配置类
| 自动配置类 | 作用 | 触发条件 |
|---|---|---|
| DataSourceAutoConfiguration | 配置数据源 | 类路径下有 DataSource 类 |
| WebMvcAutoConfiguration | 配置 Spring MVC | 类路径下有 Servlet 类 |
| RedisAutoConfiguration | 配置 Redis | 类路径下有 RedisClient 类 |
| JpaRepositoriesAutoConfiguration | 配置 JPA 仓库 | 类路径下有 JPA 相关类 |
| SecurityAutoConfiguration | 配置 Spring Security | 类路径下有 Security 类 |
| JacksonAutoConfiguration | 配置 JSON 序列化 | 类路径下有 ObjectMapper 类 |
小结
本章详细介绍了 Spring Boot 自动配置的原理:
- 核心注解:
@SpringBootApplication、@EnableAutoConfiguration的工作原理 - 加载机制:通过
AutoConfigurationImportSelector加载配置类 - 条件注解:
@ConditionalOnClass、@ConditionalOnBean等条件判断机制 - 自定义配置:创建自定义自动配置类和 Starter
- 调试技巧:查看自动配置报告,排除不需要的配置
理解自动配置原理,可以帮助我们更好地使用 Spring Boot,在遇到配置问题时也能快速定位和解决。