跳到主要内容

Spring Boot 自动配置原理

Spring Boot 的核心思想是"约定优于配置",通过自动配置大幅减少了 Spring 应用的配置工作。理解自动配置原理,有助于我们更好地使用 Spring Boot,也能在遇到问题时快速定位和解决。

自动配置概述

什么是自动配置?

在传统的 Spring 应用中,我们需要手动配置大量的 Bean。例如,要使用数据源,需要配置 DataSourceJdbcTemplateTransactionManager 等。对于一个简单的数据库应用,配置代码可能需要几十行。

Spring Boot 的自动配置机制会根据项目中的依赖、已定义的 Bean、配置属性等条件,自动创建和配置这些 Bean。我们只需要添加相应的依赖,Spring Boot 就会自动完成大部分配置工作。

自动配置的优势

减少配置工作量:大部分常用功能都有默认配置,开箱即用。

统一配置风格:通过 application.propertiesapplication.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 通过以下步骤加载自动配置类:

  1. 读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件(Spring Boot 2.7+)

  2. 在旧版本中读取 META-INF/spring.factories 文件中 EnableAutoConfiguration 对应的配置类

  3. 对配置类进行条件过滤,只加载满足条件的配置类

例如,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();
}
}

这个配置类只有在以下条件都满足时才会生效:

  • 类路径下存在 DataSourceEmbeddedDatabaseType
  • 容器中还没有 DataSource Bean
  • 配置属性 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 {
}
}

这个配置类的逻辑是:

  1. 只有当类路径下存在 DataSource 类时才会加载
  2. 使用 @EnableConfigurationProperties 绑定配置属性
  3. 根据条件选择嵌入式数据库或连接池数据源

配置属性绑定

自动配置类通常与配置属性类配合使用:

@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 自动配置的原理:

  1. 核心注解@SpringBootApplication@EnableAutoConfiguration 的工作原理
  2. 加载机制:通过 AutoConfigurationImportSelector 加载配置类
  3. 条件注解@ConditionalOnClass@ConditionalOnBean 等条件判断机制
  4. 自定义配置:创建自定义自动配置类和 Starter
  5. 调试技巧:查看自动配置报告,排除不需要的配置

理解自动配置原理,可以帮助我们更好地使用 Spring Boot,在遇到配置问题时也能快速定位和解决。