个人网站

“约定优于配置”(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 或排除自动配置来覆盖。

加分回答

  1. @ConditionalOnMissingBean 的意义:这是约定优于配置的技术保障——自动配置类注册默认 Bean 前先检查容器中是否已有,有则跳过。你的自定义 Bean 永远优先于默认 Bean
  2. Spring Boot 的" opiniated"(有主见的):Spring Boot 替你做了很多技术选型决策(HikariCP 优于 DBCP、Lettuce 优于 Jedis、Logback 优于 Log4j),这些选择都是经过社区验证的最佳实践
  3. 与 Ruby on Rails 的渊源:约定优于配置的理念最早来自 Rails,Spring Boot 借鉴了这个思想,但保留了 Spring 的灵活性——约定不是强制

面试官点评

这道题考的是你对 Spring Boot 设计哲学的理解。能说出"减少配置"是表面,面试官想听的是:具体约定了什么约定能不能改@ConditionalOnMissingBean 是怎么保证"你自定义的优先"的。如果能把 Spring Boot 的"有主见"特性也讲出来,说明你理解了它的价值主张。

原文阅读


内容有帮助?点赞、收藏、关注三连!评论区等你 💪

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐