核心概念
约定优于配置(Convention Over Configuration,CoC)是 Spring Boot 的核心设计理念,其本质是通过预设合理的默认值和标准化的项目结构,减少开发者需要显式配置的内容。
核心价值:
- 降低学习成本:新手无需了解所有配置项,遵循约定即可快速上手
- 提升开发效率:80% 场景使用默认配置,只有 20% 特殊需求需自定义
- 统一团队规范:所有 Spring Boot 项目遵循相同结构,降低维护成本
- 减少配置错误:默认配置经过社区验证,避免常见的配置陷阱
对比:
- 传统 Spring:需要显式配置所有 Bean、扫描路径、端口、数据源等
- Spring Boot:约定优先,只在需要覆盖默认值时才配置
原理与关键点
1. 自动配置的约定
Spring Boot 内置 200+ 自动配置类,每个类都包含合理的默认设置。
案例 1:Web 服务器端口
// 约定:默认端口 8080
// 无需配置,直接启动
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
查看源码 ServerProperties:
@ConfigurationProperties(prefix = "server")
public class ServerProperties {
private Integer port = 8080; // 约定默认端口
// ...
}
自定义配置(覆盖约定):
server:
port: 9090 # 仅在需要时修改
案例 2:静态资源路径
约定将静态资源放在以下目录:
src/main/resources/staticsrc/main/resources/publicsrc/main/resources/META-INF/resources
访问 http://localhost:8080/css/style.css 会自动映射到 static/css/style.css。
源码体现(WebMvcAutoConfiguration):
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
return;
}
// 约定的静态资源路径
registry.addResourceHandler("/**")
.addResourceLocations(CLASSPATH_RESOURCE_LOCATIONS);
}
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
};
2. 项目结构的约定
Spring Boot 推荐的标准结构:
src/
├── main/
│ ├── java/
│ │ └── com.example.demo/
│ │ ├── DemoApplication.java # 主类(约定放在根包)
│ │ ├── controller/ # 控制层(约定命名)
│ │ ├── service/ # 业务层
│ │ ├── repository/ # 数据访问层
│ │ └── domain/ # 实体类
│ └── resources/
│ ├── application.yml # 配置文件(约定名称)
│ ├── static/ # 静态资源
│ └── templates/ # 模板文件
└── test/ # 测试代码
关键约定:
- 主类位置:
@SpringBootApplication放在根包,自动扫描子包组件 - 配置文件名:
application.yml或application.properties - 多环境配置:
application-{profile}.yml(如application-prod.yml)
3. 条件化自动配置
Spring Boot 通过条件注解实现”约定但可覆盖”:
@Configuration
@ConditionalOnClass(DataSource.class) // 约定:Classpath 有 DataSource 时生效
@ConditionalOnMissingBean(DataSource.class) // 可覆盖:用户未定义才使用默认
public class DataSourceAutoConfiguration {
@Bean
public DataSource dataSource(DataSourceProperties properties) {
// 约定:使用 HikariCP 作为默认连接池
return properties.initializeDataSourceBuilder()
.type(HikariDataSource.class)
.build();
}
}
设计精髓:
@ConditionalOnMissingBean:用户自定义 Bean 优先级更高@ConditionalOnProperty:通过配置开关自动配置
性能与实践考量
1. 约定的性能优化
默认连接池:HikariCP
Spring Boot 约定使用性能最优的 HikariCP:
spring:
datasource:
hikari:
maximum-pool-size: 10 # 默认 10
connection-timeout: 30000 # 默认 30s
默认 JSON 序列化:Jackson
约定使用 Jackson,自动配置最佳实践:
@RestController
public class UserController {
@GetMapping("/user")
public User getUser() {
return new User("张三", 25); // 自动序列化为 JSON
}
}
2. 覆盖约定的场景
场景 1:切换嵌入式容器
<!-- 排除默认的 Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 使用性能更高的 Undertow -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
场景 2:自定义日志格式
logging:
pattern:
console: "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
level:
com.example: DEBUG # 覆盖默认的 INFO 级别
3. 分布式场景的约定
约定的配置中心命名规范(Nacos):
spring:
application:
name: order-service # 约定:服务名作为配置 dataId
cloud:
nacos:
config:
file-extension: yaml # 约定:配置格式
group: DEFAULT_GROUP # 约定:默认分组
Nacos 配置中心会自动读取 order-service.yaml 配置。
面试答题总结
为什么约定优于配置?
- 提升开发效率:
- 传统 Spring 项目搭建需配置 XML、端口、扫描路径等(100+ 行配置)
- Spring Boot 零配置启动,只需
@SpringBootApplication+main方法
- 降低学习曲线:
- 新手无需理解 Servlet 容器部署、依赖管理、事务配置等复杂概念
- 遵循约定的目录结构和命名规范即可上手
- 统一团队规范:
- 所有 Spring Boot 项目结构一致,降低团队协作成本
- 新成员快速熟悉项目(约定即文档)
- 减少配置错误:
- 默认配置经过社区验证(如 HikariCP 的最优参数)
- 避免常见错误(如忘记配置事务管理器)
核心机制:
- 自动配置:
@Conditional条件注解 + 默认值 - 覆盖机制:
@ConditionalOnMissingBean保证用户配置优先 - 外部化配置:
application.yml统一管理可变参数
典型约定:
- 端口 8080、静态资源路径
/static - HikariCP 连接池、Jackson JSON 序列化
- 主类放根包、多环境配置
application-{profile}.yml
何时覆盖约定:
- 性能调优(如连接池大小、线程池参数)
- 特殊需求(如切换容器、自定义序列化)
- 企业规范(如统一日志格式、监控集成)
原则:
- 80% 场景使用约定(快速开发)
- 20% 场景覆盖约定(满足定制需求)
- 避免过度配置(YAGNI 原则:You Aren’t Gonna Need It)