核心概念

约定优于配置(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/static
  • src/main/resources/public
  • src/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.ymlapplication.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 配置。


面试答题总结

为什么约定优于配置?

  1. 提升开发效率
    • 传统 Spring 项目搭建需配置 XML、端口、扫描路径等(100+ 行配置)
    • Spring Boot 零配置启动,只需 @SpringBootApplication + main 方法
  2. 降低学习曲线
    • 新手无需理解 Servlet 容器部署、依赖管理、事务配置等复杂概念
    • 遵循约定的目录结构和命名规范即可上手
  3. 统一团队规范
    • 所有 Spring Boot 项目结构一致,降低团队协作成本
    • 新成员快速熟悉项目(约定即文档)
  4. 减少配置错误
    • 默认配置经过社区验证(如 HikariCP 的最优参数)
    • 避免常见错误(如忘记配置事务管理器)

核心机制

  • 自动配置@Conditional 条件注解 + 默认值
  • 覆盖机制@ConditionalOnMissingBean 保证用户配置优先
  • 外部化配置application.yml 统一管理可变参数

典型约定

  • 端口 8080、静态资源路径 /static
  • HikariCP 连接池、Jackson JSON 序列化
  • 主类放根包、多环境配置 application-{profile}.yml

何时覆盖约定

  • 性能调优(如连接池大小、线程池参数)
  • 特殊需求(如切换容器、自定义序列化)
  • 企业规范(如统一日志格式、监控集成)

原则

  • 80% 场景使用约定(快速开发)
  • 20% 场景覆盖约定(满足定制需求)
  • 避免过度配置(YAGNI 原则:You Aren’t Gonna Need It)