Spring Boot 自动配置原理
1. 自动配置概述
1.1 什么是自动配置
Spring Boot 的自动配置机制会根据项目中的依赖、类路径下的类、已定义的 Bean 等条件,自动配置 Spring 应用所需的各种 Bean。
1.2 自动配置的优势
| 优势 | 说明 |
|---|---|
| 减少配置 | 无需手动配置大量 Bean |
| 约定优于配置 | 遵循最佳实践 |
| 快速开发 | 专注于业务逻辑 |
| 灵活定制 | 可随时覆盖默认配置 |
2. @SpringBootApplication 注解
2.1 注解组成
java
@SpringBootApplication
public class MyappApplication {
public static void main(String[] args) {
SpringApplication.run(MyappApplication.class, args);
}
}等价于:
java
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
public class MyappApplication {
public static void main(String[] args) {
SpringApplication.run(MyappApplication.class, args);
}
}2.2 注解详解
| 注解 | 说明 |
|---|---|
| @SpringBootConfiguration | 表示这是一个配置类 |
| @EnableAutoConfiguration | 启用自动配置 |
| @ComponentScan | 组件扫描 |
2.3 自定义扫描
java
@SpringBootApplication(scanBasePackages = "com.example")
public class MyappApplication {
public static void main(String[] args) {
SpringApplication.run(MyappApplication.class, args);
}
}
// 或排除特定自动配置
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class MyappApplication {
public static void main(String[] args) {
SpringApplication.run(MyappApplication.class, args);
}
}3. @EnableAutoConfiguration 原理
3.1 导入自动配置类
java
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}3.2 AutoConfigurationImportSelector
java
public class AutoConfigurationImportSelector implements DeferredImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry =
getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(
autoConfigurationEntry.getConfigurations()
);
}
protected AutoConfigurationEntry getAutoConfigurationEntry(
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(
annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(
annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}3.3 自动配置文件
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports内容示例:
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.RedisAutoConfiguration4. 条件注解
4.1 条件注解分类
| 注解 | 说明 |
|---|---|
| @ConditionalOnClass | 类路径存在指定类时生效 |
| @ConditionalOnMissingClass | 类路径不存在指定类时生效 |
| @ConditionalOnBean | 容器中存在指定 Bean 时生效 |
| @ConditionalOnMissingBean | 容器中不存在指定 Bean 时生效 |
| @ConditionalOnProperty | 配置属性满足条件时生效 |
| @ConditionalOnWebApplication | Web 应用时生效 |
| @ConditionalOnNotWebApplication | 非 Web 应用时生效 |
| @ConditionalOnExpression | SpEL 表达式为 true 时生效 |
| @ConditionalOnJava | Java 版本满足条件时生效 |
| @ConditionalOnResource | 资源存在时生效 |
| @ConditionalOnSingleCandidate | 容器中只有一个指定 Bean 时生效 |
4.2 @ConditionalOnClass
java
@Configuration
@ConditionalOnClass(DataSource.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}4.3 @ConditionalOnBean
java
@Configuration
@ConditionalOnClass(DataSource.class)
public class JdbcTemplateAutoConfiguration {
@Bean
@ConditionalOnMissingBean(JdbcOperations.class)
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}4.4 @ConditionalOnProperty
java
@Configuration
@ConditionalOnClass(RedisClient.class)
@ConditionalOnProperty(name = "spring.data.redis.enabled",
havingValue = "true",
matchIfMissing = true)
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RedisTemplate<String, Object> redisTemplate(
RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
return template;
}
}4.5 @ConditionalOnWebApplication
java
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
public class WebMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DispatcherServlet dispatcherServlet() {
return new DispatcherServlet();
}
}4.6 组合条件
java
@Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnSingleCandidate(DataSource.class)
@ConditionalOnMissingBean({ SqlSessionFactory.class, SqlSessionTemplate.class })
public class MybatisAutoConfiguration {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
return factory.getObject();
}
}5. 自动配置类分析
5.1 DataSourceAutoConfiguration
java
@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 {
}
}5.2 WebMvcAutoConfiguration
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class,
TaskExecutionAutoConfiguration.class,
ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix(this.mvcProperties.getView().getPrefix());
resolver.setSuffix(this.mvcProperties.getView().getSuffix());
return resolver;
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledResourceChain
public ResourceHandlerRegistration resourceHandlerRegistration() {
// ...
}
}5.3 RedisAutoConfiguration
java
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RedisClient.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(RedisConnectionDetails.class)
public PropertiesRedisConnectionDetails redisConnectionDetails(
RedisProperties properties) {
return new PropertiesRedisConnectionDetails(properties);
}
@Bean
@ConditionalOnMissingBean(RedisTemplate.class)
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}6. 配置属性绑定
6.1 @ConfigurationProperties
java
@ConfigurationProperties(prefix = "spring.datasource")
public class DataSourceProperties {
private String url;
private String username;
private String password;
private String driverClassName;
private String type;
private Hikari hikari = new Hikari();
// getters and setters
public static class Hikari {
private String connectionTimeout;
private String maximumPoolSize;
private String minimumIdle;
// getters and setters
}
}6.2 启用配置属性
java
@Configuration
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceConfig {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}6.3 配置文件
yaml
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: password
driver-class-name: com.mysql.cj.jdbc.Driver
hikari:
connection-timeout: 30000
maximum-pool-size: 10
minimum-idle: 57. 自定义 Starter
7.1 创建 Starter 项目
my-spring-boot-starter/
├── src/main/java/
│ └── com/example/starter/
│ ├── MyService.java
│ ├── MyProperties.java
│ └── MyAutoConfiguration.java
└── src/main/resources/
└── META-INF/spring/
└── org.springframework.boot.autoconfigure.AutoConfiguration.imports7.2 自动配置类
java
@Configuration
@ConditionalOnClass(MyService.class)
@EnableConfigurationProperties(MyProperties.class)
public class MyAutoConfiguration {
private final MyProperties properties;
public MyAutoConfiguration(MyProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public MyService myService() {
return new MyService(properties);
}
}7.3 配置属性类
java
@ConfigurationProperties(prefix = "my.service")
public class MyProperties {
private String name = "default";
private boolean enabled = true;
private int timeout = 30000;
// getters and setters
}7.4 注册自动配置
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.starter.MyAutoConfiguration7.5 条件注解顺序
java
@Configuration
@ConditionalOnClass(MyService.class)
@ConditionalOnProperty(prefix = "my.service", name = "enabled",
havingValue = "true", matchIfMissing = true)
@AutoConfigureBefore(AnotherAutoConfiguration.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MyAutoConfiguration {
// ...
}8. 排除自动配置
8.1 注解排除
java
@SpringBootApplication(exclude = {
DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class
})
public class MyappApplication {
public static void main(String[] args) {
SpringApplication.run(MyappApplication.class, args);
}
}
// 或排除类名
@SpringBootApplication(excludeName = {
"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration"
})
public class MyappApplication {
public static void main(String[] args) {
SpringApplication.run(MyappApplication.class, args);
}
}8.2 配置文件排除
yaml
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
- org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration8.3 条件排除
java
@Configuration
@ConditionalOnProperty(name = "my.feature.enabled", havingValue = "false")
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class ExcludeDataSourceConfiguration {
// 禁用数据源自动配置
}9. 调试自动配置
9.1 启动日志
yaml
debug: true9.2 Actuator 端点
yaml
management:
endpoints:
web:
exposure:
include: conditions访问 /actuator/conditions 查看自动配置报告。
9.3 查看匹配条件
java
@SpringBootApplication
public class MyappApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(MyappApplication.class, args);
ConditionEvaluationReport report =
context.getBeanFactory().getBean(
AutoConfigurationReport.class,
ConditionEvaluationReport.class
);
report.getConditionAndOutcomesBySource().forEach((source, outcomes) -> {
System.out.println(source + ": " + outcomes);
});
}
}10. 最佳实践
10.1 合理使用条件注解
java
@Configuration
@ConditionalOnClass(DataSource.class)
@EnableConfigurationProperties(DataSourceProperties.class)
public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.datasource", name = "url")
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}10.2 使用 @AutoConfigureOrder
java
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class FirstAutoConfiguration {
// 最先加载
}
@Configuration
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public class LastAutoConfiguration {
// 最后加载
}10.3 提供默认值
java
@ConfigurationProperties(prefix = "my.service")
public class MyProperties {
private String name = "default-name";
private int port = 8080;
private Duration timeout = Duration.ofSeconds(30);
// getters and setters
}11. 小结
本章学习了 Spring Boot 自动配置的核心原理:
| 内容 | 要点 |
|---|---|
| @SpringBootApplication | 组合注解、组件扫描 |
| @EnableAutoConfiguration | 导入自动配置类 |
| 条件注解 | @ConditionalOnXxx |
| 自动配置文件 | AutoConfiguration.imports |
| 配置属性绑定 | @ConfigurationProperties |
| 自定义 Starter | 创建、注册、条件配置 |
| 排除配置 | exclude、配置文件 |
| 调试配置 | debug、actuator |
下一章将学习 Spring Boot 4 的 Web 开发。