SpringBoot 自动配置原理
SpringBoot 自动配置原理
当我们需要用到Redis时,在pom.xml文件加了spring-boot-starter-data-redis依赖,在application.yml里写了几行 Redis 的配置,直接注入StringRedisTemplate就能用。不用手动创建连接工厂,也不用配置序列化器。这些"不用我们操心"的配置,到底是怎么出现的?Spring Boot 怎么知道我们的项目需要哪些 Bean,又怎么决定用什么参数来创建它们?
目录
- SpringBoot自动装配
- 自动配置的加载机制
- 条件装配:不是所有配置都会生效
- 一个具体例子:HttpMessageConverter
- 和手动配置的对比
- 小结
SpringBoot自动装配
Spring Boot 的自动配置,本质上就是在启动时扫描一堆"配置类",根据当前项目的依赖和环境条件,决定哪些配置类该生效、哪些该跳过。
一切的起点是@SpringBootApplication注解。它不只是一个"启动注解"。把它展开是长这样:
@SpringBootApplication// 等价于以下三个注解的组合@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan三个注解各管各的:
| 注解 | 职责 |
|---|---|
@SpringBootConfiguration | 标记这是一个配置类(本质就是@Configuration) |
@ComponentScan | 扫描当前包及子包下的@Component、@Service、@Controller等 |
@EnableAutoConfiguration | 开启自动配置,这是今天的主角 |
@ComponentScan负责把你手动写的 Bean 扫描进来,而@EnableAutoConfiguration负责把 Spring Boot 预定义的配置类加载进来。两者互不干涉,各干各的活。
自动配置的加载机制
@EnableAutoConfiguration是怎么找到那些配置类的?答案在注解的实现里。
打开@EnableAutoConfiguration的源码,我们会看到这么一行:
@Import(AutoConfigurationImportSelector.class)@Import是 Spring 提供的注解,作用是把指定的类导入到 Spring 容器中。这里导入的是AutoConfigurationImportSelector,它是自动配置的核心调度员。
AutoConfigurationImportSelector做了一件关键的事:去读一个叫META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports的文件(Spring Boot 3.x),或者META-INF/spring.factories(Spring Boot 2.x)。这个文件里列了一大堆配置类的全限定名。
加载流程可以用一张图概括:
这些配置类从哪来呢?答案是starter。当你引入spring-boot-starter-web时,这个 starter 的 jar 包里就包含了对应的自动配置类。Starter 不只是"引入依赖",它还带来了配置逻辑。
条件装配
自动配置类被加载后,不会全部生效。Spring Boot 用了一套条件注解来控制"这个配置类到底要不要生效"。这套机制叫条件装配(Conditional Assembly)。
核心注解有这几个:
| 注解 | 含义 | 例子 |
|---|---|---|
@ConditionalOnClass | classpath 中存在某个类时才生效 | 有DataSource类才配置数据源 |
@ConditionalOnMissingBean | 容器中没有某个 Bean 时才生效 | 你自己没配DataSource,Spring Boot 才帮你配 |
@ConditionalOnProperty | 配置文件中有某个属性时才生效 | spring.redis.host存在才配置 Redis |
@ConditionalOnMissingClass | classpath 中不存在某个类时才生效 | 没有某个类才走这个分支 |
@ConditionalOnMissingBean是最关键的一个。它保证了一件事:如果你自己手动配了一个 Bean,Spring Boot 就不会再帮你配了。你手动配置的优先级永远高于自动配置。这就是"约定优于配置"的具体体现,Spring Boot 给你一个默认值,但你随时可以覆盖。
举个例子:
@Configuration@ConditionalOnClass(DataSource.class)// classpath 里有 DataSource 类才生效publicclassDataSourceAutoConfiguration{@Bean@ConditionalOnMissingBean// 容器里没有 DataSource Bean 才创建publicDataSourcedataSource(){// 用配置文件里的属性创建默认数据源returnnewHikariDataSource(...);}}这段逻辑的意思是:如果项目里引入了数据库驱动(DataSource.class存在),而且你没有自己手动配DataSourceBean,那 Spring Boot 就用默认参数帮你创建一个 HikariCP 连接池。
但如果你在自己的@Configuration类里写了一个@Bean DataSource,Spring Boot 的这个自动配置就会被跳过。你的手动配置优先。
一个具体例子:HttpMessageConverter
来看一个你每天都在用、但可能没注意过的自动配置。
当开发者写@RequestBody接收 JSON 时,Spring Boot 需要把 JSON 字符串转成 Java 对象。这个转换靠的是HttpMessageConverter。我们从来没有手动注册过MappingJackson2HttpMessageConverter,但它就在容器里了。
因为JacksonAutoConfiguration帮我们做了注册。展开来看:
@AutoConfiguration@ConditionalOnClass(ObjectMapper.class)// classpath 里有 Jackson 才生效publicclassJacksonAutoConfiguration{@Bean@ConditionalOnMissingBean// 没自己配 ObjectMapper 才创建publicObjectMapperjacksonObjectMapper(){returnnewObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL).disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);}}这个配置类的生效条件是:classpath 中存在ObjectMapper.class(也就是你引入了 Jackson 依赖)。当你引入了spring-boot-starter-web,Jackson 作为传递依赖被拉进来,ObjectMapper.class就在 classpath 中了。于是这个配置类生效,帮你创建了一个默认的ObjectMapperBean。
如果你想自定义 ObjectMapper 的行为(比如日期格式、空值策略),只需要自己写一个@Bean ObjectMapper,Spring Boot 的自动配置就会让位。不需要去改任何 Spring Boot 的代码。
和手动配置的对比
既然自动配置这么方便,我们还需要手动写@Configuration吗?
| 对比维度 | 自动配置 | 手动配置 |
|---|---|---|
| 谁写的 | Spring Boot / Starter 作者 | 开发者自己 |
| 生效条件 | 有依赖 + 没被覆盖 → 自动生效 | 写了就生效 |
| 优先级 | 低(有@ConditionalOnMissingBean) | 高(手动配置会覆盖自动配置) |
| 适用场景 | 通用默认配置 | 个性化需求、业务特有配置 |
| 可控性 | 黑盒,只能通过配置文件调参数 | 白盒,完全开发者控制 |
自动配置解决的是"80% 的通用场景",手动配置解决的是"20% 的个性化需求"。两者不矛盾,而是配合关系。Spring Boot 给的是一个能跑的默认值,开发者根据业务需要去覆盖它。
最理想的状态是:项目里几乎不写@Configuration,大部分配置靠 starter + yml 搞定。只有遇到 starter 覆盖不了的场景(比如多数据源、自定义序列化、特殊的 Bean 初始化逻辑),才手动写配置类。
小结
Spring Boot 自动配置机制可以概括为:启动时读取 starter 提供的配置类清单,逐一检查其条件注解;满足条件的配置会被注册到容器中,而用户手动定义的 Bean 则会优先覆盖自动配置。本质上,它就是一份配置类列表 + 一套条件判断机制。当你引入某个依赖后,SpringBoot会识别对应的自动配置类;如果条件满足,就自动创建相关 Bean;如果用户没有显式配置,则使用框架提供的默认配置。
