当前位置: 首页 > news >正文

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)

核心注解有这几个:

注解含义例子
@ConditionalOnClassclasspath 中存在某个类时才生效DataSource类才配置数据源
@ConditionalOnMissingBean容器中没有某个 Bean 时才生效你自己没配DataSource,Spring Boot 才帮你配
@ConditionalOnProperty配置文件中有某个属性时才生效spring.redis.host存在才配置 Redis
@ConditionalOnMissingClassclasspath 中不存在某个类时才生效没有某个类才走这个分支

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

http://www.jsqmd.com/news/1105969/

相关文章:

  • 记忆排列题目分析
  • 第93题 IGBT模块陶瓷基板(AlN/Al₂O₃/Si₃N₄)金属化
  • C++ PDF解析渲染库Poppler全方位实战:场景、库对比、CMake集成、可运行代码
  • 死磕信号量实现读者-写者:我被自己写的代码坑惨了
  • 市县级全域旅游智慧导览电子地图制作实操(三)AI+人工生成全域手绘地图
  • Xinference开源大模型本地部署实战指南
  • 工业级条码扫描模块与PIC32MZ嵌入式方案解析
  • 3分钟掌握Illustrator智能填充:Fillinger脚本让你的设计效率翻倍
  • 网络流量分类技术:从机器学习到硬件优化实践
  • UABEA:重新定义Unity资源编辑的跨平台革命
  • 迅雷网盘在线解析:高速直链下载的方法
  • 目标检测分类部分损失函数:BCE → Focal Loss → VFL → MAL 的演进
  • okbiye 毕业论文 AI 写作实操指南|界面全功能拆解,一站式搞定学位论文撰写
  • UE5快捷键速查
  • 主流VST头显视觉性能对比:Vision Pro、Quest 3与Quest Pro评测
  • 大厂高频面试题:手机号加密存储后,如何快速按尾号查询?
  • AI一周事件 · 2026-W27(6月24日–6月30日)
  • 终极Windows驱动管理指南:DriverStoreExplorer免费释放C盘空间
  • BetterNCM Installer:3步解锁网易云音乐隐藏功能
  • 为了防止题目链接失效,将题目原文复制如下:
  • 基于 epoll 的协程调度器——零基础深入浅出 C++20 协程
  • 7_CSS预处理器Sass
  • Sonnet 5 发布:Prompt 已死,Loop 当
  • Java实现Navicat密码加密解密:AES-256-CBC本地安全存储实战
  • 短效代理适合哪些业务场景?资深玩家实测科普适配场景指南
  • 使用74HC165与ARM Cortex-M4实现高效并行转串行输入设计
  • QuickVina 2深度解析:20倍加速的分子对接性能揭秘
  • IS31FL3731 LED驱动芯片与PIC18F24K50微控制器的嵌入式开发实践
  • 【精通】SmartWriter v2.5:写作平台 CI/CD — 提示词版本管理、A/B 评测与回归验证深度实战
  • Go 进阶必修:90% 的人都没用对的“表驱动法”