孤舟笔记 Spring全家桶篇二十一 如何理解Spring Boot约定优于配置?这些约定你知道几个
文章目录
个人网站
“约定优于配置”(Convention over Configuration)是 Spring Boot 的灵魂,但很多人对它的理解停留在"少写配置"的层面。面试官问这题,他想听的是:Spring Boot 到底约定了什么?这些约定能不能改?改了会怎样?
先说结论
| 维度 | 说明 |
|---|---|
| 核心思想 | 有合理的默认值,开发者只需关注不符合约定的部分 |
| 目的 | 减少决策成本,提升开发效率 |
| 体现 | 默认配置、默认目录结构、默认 Bean、默认行为 |
| 灵活性 | 约定可覆盖,通过配置文件或自定义 Bean |
一句话记住:约定优于配置就是"公司有规章制度,不用你每次都问该怎么办"——按规矩来就行,想改也行
什么是约定优于配置
想象你入职一家公司:
- 没有约定:每天上班,你都得问"工位在哪?电脑用什么系统?代码放哪个目录?"——效率极低
- 有约定:工位按部门分配、电脑统一 macOS、代码放 ~/projects/——不用问,直接干
- 想改也行:你可以申请换工位、用自己电脑、改代码目录——但得主动申请
Spring Boot 就是这家"有约定的公司"。
Spring Boot的核心约定
约定一:项目结构
src/main/java/com/example/
├── Application.java ← 启动类,放在根包
├── controller/ ← Controller 放这里
├── service/ ← Service 放这里
├── repository/ ← DAO 放这里
└── config/ ← 配置类放这里
src/main/resources/
├── application.yml ← 配置文件,固定名字
├── static/ ← 静态资源
└── templates/ ← 模板文件
你不按这个结构来也能跑,但按约定来最省事——@ComponentScan 从主类所在包开始扫描,放对了位置自动被扫描到。
约定二:默认配置
| 配置项 | 默认值 | 修改方式 |
|---|---|---|
| 端口 | 8080 | server.port=9090 |
| 上下文路径 | / | server.servlet.context-path=/api |
| 数据源 | H2 内存数据库 | spring.datasource.* |
| 日志级别 | INFO | logging.level.root=DEBUG |
| JSON 日期格式 | 时间戳 | spring.jackson.date-format=yyyy-MM-dd |
不用配任何东西,一个 @SpringBootApplication 就能启动 Web 服务。想改?在 application.yml 里写一行覆盖。
约定三:默认Bean
Spring Boot 的自动配置类会按条件注册默认 Bean:
@AutoConfiguration
@ConditionalOnClass(DataSource.class) // 👈 classpath 有 DataSource 才生效
@ConditionalOnMissingBean(DataSource.class) // 👈 你没自定义才注册默认的
public class DataSourceAutoConfiguration {
@Bean
public DataSource dataSource() {
return new HikariDataSource(); // 👈 默认 HikariCP
}
}
关键注解:@ConditionalOnMissingBean——你自定义了就用你的,没有才用默认的。这就是约定优于配置的精髓:默认给你最好的,你想换随时换。
约定四:Starter命名约定
| Starter | 功能 |
|---|---|
| spring-boot-starter-web | Web 开发 |
| spring-boot-starter-data-jpa | JPA 数据访问 |
| spring-boot-starter-data-redis | Redis |
| spring-boot-starter-security | 安全认证 |
官方 Starter 命名:spring-boot-starter-*
第三方 Starter 命名:*-spring-boot-starter
看到名字就知道干什么的,这也是约定。
约定能改吗?当然能
Spring Boot 的约定 不是强制,而是默认值。三种覆盖方式:
1. 配置文件覆盖
server:
port: 9090 # 👈 覆盖默认的 8080
2. 自定义 Bean 覆盖
@Configuration
public class MyConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
// 👈 你的 ObjectMapper 替代默认的
}
}
3. 排除自动配置
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
// 👈 不要数据源的自动配置
约定优于配置的代价
| 优点 | 代价 |
|---|---|
| 快速开发 | 默认行为可能不符合需求 |
| 减少样板代码 | 出问题时排查困难(不知道默认配了什么) |
| 团队统一 | 过度依赖约定,缺乏理解 |
就像公司规章提高效率,但新员工可能不理解"为什么这样做"。Spring Boot 的约定也是如此——用起来爽,但出了问题得理解底层才能排查。
约定优于配置全景
核心约定
├── 项目结构 —— 包扫描从主类根包开始
├── 默认配置 —— 端口8080、H2数据库、HikariCP
├── 默认Bean —— @ConditionalOnMissingBean 条件注册
├── Starter命名 —— spring-boot-starter-* 官方命名
└── 配置文件 —— application.yml 固定名称
覆盖方式
├── 配置文件(application.yml)
├── 自定义 Bean(@ConditionalOnMissingBean)
└── 排除自动配置(exclude)
口诀:约定优于配置好,默认值来开箱跑;
端口8080数据源,想改随时能覆盖;
自定义Bean优先级高,排除配置也能搞;
省心省力效率高,出了问题得懂行
回答技巧与点评
标准回答:约定优于配置是 Spring Boot 的核心设计理念,指框架提供合理的默认值和规范,开发者只需关注不符合约定的部分。Spring Boot 的约定包括:默认端口 8080、默认内嵌 Tomcat、默认 HikariCP 数据源、默认包扫描从主类所在包开始、默认配置文件 application.yml 等。这些约定都可以通过配置文件、自定义 Bean 或排除自动配置来覆盖。
加分回答
- @ConditionalOnMissingBean 的意义:这是约定优于配置的技术保障——自动配置类注册默认 Bean 前先检查容器中是否已有,有则跳过。你的自定义 Bean 永远优先于默认 Bean
- Spring Boot 的" opiniated"(有主见的):Spring Boot 替你做了很多技术选型决策(HikariCP 优于 DBCP、Lettuce 优于 Jedis、Logback 优于 Log4j),这些选择都是经过社区验证的最佳实践
- 与 Ruby on Rails 的渊源:约定优于配置的理念最早来自 Rails,Spring Boot 借鉴了这个思想,但保留了 Spring 的灵活性——约定不是强制
面试官点评
这道题考的是你对 Spring Boot 设计哲学的理解。能说出"减少配置"是表面,面试官想听的是:具体约定了什么、约定能不能改、@ConditionalOnMissingBean 是怎么保证"你自定义的优先"的。如果能把 Spring Boot 的"有主见"特性也讲出来,说明你理解了它的价值主张。
内容有帮助?点赞、收藏、关注三连!评论区等你 💪
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐
所有评论(0)