SpringBoot 自动配置原理

SpringBoot 自动配置原理

当我们需要用到Redis时,在pom.xml文件加了 spring-boot-starter-data-redis依赖,在 application.yml 里写了几行 Redis 的配置,直接注入 StringRedisTemplate 就能用。不用手动创建连接工厂,也不用配置序列化器。这些"不用我们操心"的配置,到底是怎么出现的?Spring Boot 怎么知道我们的项目需要哪些 Bean,又怎么决定用什么参数来创建它们?

目录

SpringBoot自动装配

Spring Boot 的自动配置,本质上就是在启动时扫描一堆"配置类",根据当前项目的依赖和环境条件,决定哪些配置类该生效、哪些该跳过。

一切的起点是 @SpringBootApplication 注解。它不只是一个"启动注解"。把它展开是长这样:

java 复制代码
@SpringBootApplication
// 等价于以下三个注解的组合
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan

三个注解各管各的:

注解 职责
@SpringBootConfiguration 标记这是一个配置类(本质就是 @Configuration
@ComponentScan 扫描当前包及子包下的 @Component@Service@Controller
@EnableAutoConfiguration 开启自动配置,这是今天的主角

@ComponentScan 负责把你手动写的 Bean 扫描进来,而 @EnableAutoConfiguration 负责把 Spring Boot 预定义的配置类加载进来。两者互不干涉,各干各的活。

自动配置的加载机制

@EnableAutoConfiguration 是怎么找到那些配置类的?答案在注解的实现里。

打开 @EnableAutoConfiguration 的源码,我们会看到这么一行:

java 复制代码
@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 给你一个默认值,但你随时可以覆盖。

举个例子:

java 复制代码
@Configuration
@ConditionalOnClass(DataSource.class)  // classpath 里有 DataSource 类才生效
public class DataSourceAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean  // 容器里没有 DataSource Bean 才创建
    public DataSource dataSource() {
        // 用配置文件里的属性创建默认数据源
        return new HikariDataSource(...);
    }
}

这段逻辑的意思是:如果项目里引入了数据库驱动(DataSource.class 存在),而且你没有自己手动配 DataSource Bean,那 Spring Boot 就用默认参数帮你创建一个 HikariCP 连接池。

但如果你在自己的 @Configuration 类里写了一个 @Bean DataSource,Spring Boot 的这个自动配置就会被跳过。你的手动配置优先。

一个具体例子:HttpMessageConverter

来看一个你每天都在用、但可能没注意过的自动配置。

当开发者写 @RequestBody 接收 JSON 时,Spring Boot 需要把 JSON 字符串转成 Java 对象。这个转换靠的是 HttpMessageConverter。我们从来没有手动注册过 MappingJackson2HttpMessageConverter,但它就在容器里了。

因为 JacksonAutoConfiguration 帮我们做了注册。展开来看:

java 复制代码
@AutoConfiguration
@ConditionalOnClass(ObjectMapper.class)  // classpath 里有 Jackson 才生效
public class JacksonAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean  // 没自己配 ObjectMapper 才创建
    public ObjectMapper jacksonObjectMapper() {
        return new ObjectMapper()
            .setSerializationInclusion(JsonInclude.Include.NON_NULL)
            .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
    }
}

这个配置类的生效条件是:classpath 中存在 ObjectMapper.class(也就是你引入了 Jackson 依赖)。当你引入了 spring-boot-starter-web,Jackson 作为传递依赖被拉进来,ObjectMapper.class 就在 classpath 中了。于是这个配置类生效,帮你创建了一个默认的 ObjectMapper Bean。

如果你想自定义 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;如果用户没有显式配置,则使用框架提供的默认配置。