Skip to content

Spring Boot 快速上手:约定优于配置与 Starter

本文是 Spring 系统学习系列的 L1 入门篇。前置:IoC、DI、AOP:Spring 到底解决什么问题。学完可以配合面试题食用:Spring Boot 自动配置原理:@EnableAutoConfiguration 与 spring.factoriesSpring 条件注解

没有 Boot 的日子:配置地狱长什么样

2014 年以前搭一个能跑的 Spring MVC 项目,业务代码一行没写,先干这些活:装 Tomcat、写 web.xml 声明 DispatcherServlet、写 spring-mvc.xml 配组件扫描和视图解析器、pom 里逐个挑 spring-web/spring-webmvc/jackson 的版本并祈祷它们互相兼容。一个 Hello World 的配置量是业务代码的好几倍,而且每个新项目都要重来一遍。

Spring Boot 的解法是"约定优于配置":你引了 web 相关依赖,它默认你是个 web 应用,内嵌 Tomcat、DispatcherServlet、Jackson 全部按约定配好;约定不合口味,用自己的配置覆盖。看一个 Boot 版 Hello World:

java
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

@RestController
class HelloController {
    @GetMapping("/hello")
    String hello() {
        return "hello";
    }
}

一个类跑起来一个 web 服务,没有 XML,没有 Tomcat 安装步骤。省掉的活归了三处:依赖打包归 starter,默认配置归自动配置,服务器归内嵌容器。这篇讲前两处,自动配置的完整加载链路在后面的自动配置深潜篇展开。

starter 是什么:一包依赖加一份自动配置

拆开 spring-boot-starter-web 的 pom 看,这个模块里几乎没有代码,只有一堆依赖声明:

  • spring-web / spring-webmvc:MVC 框架本体
  • spring-boot-starter-tomcat:内嵌 Tomcat
  • spring-boot-starter-json:JSON 序列化,用的是 Jackson
  • spring-boot-starter:日志(Logback)、YAML 解析、自动配置引擎

所以 starter 不是什么新格式,就是一个普通 Maven 模块,价值在两件事。

第一,版本对齐。 spring-boot-starter-parent 里维护了几百个依赖的版本号,你引 starter 不用写版本,官方保证这套组合是测过的。依赖冲突在 Boot 项目里出现的概率大幅下降,前提是你别自己去指定一堆版本号。

第二,触发自动配置。 starter 把 jar 送进 classpath,autoconfigure 模块里的配置类用条件注解检查 classpath:"有 webmvc 的类?那我把 web 环境配起来。"整条链路是这样:

mermaid
flowchart LR
    A[pom 引入 starter-web] --> B[依赖进入 classpath<br/>webmvc / tomcat / jackson]
    B --> C[自动配置类条件命中<br/>ConditionalOnClass]
    C --> D[容器装配 Web 组件<br/>DispatcherServlet / 内嵌 Tomcat]
    D --> E[应用直接处理 HTTP 请求]

条件注解是这套机制的开关:@ConditionalOnClass(classpath 有某类才生效)、@ConditionalOnMissingBean(容器里没有才注册)、@ConditionalOnProperty(配置了某属性才生效)。每个自动配置类都挂着一排这样的注解,所谓"约定"就是被它们一行行写死的。哪些条件注解、各自怎么判定,在面试题篇条件注解有展开。

常用注解速查:装配你的 Bean

Boot 应用里写业务,大部分时间只跟几个注解打交道。@SpringBootApplication 点开是三个注解的组合:

  • @SpringBootConfiguration:本质是 @Configuration,声明这是个配置类
  • @EnableAutoConfiguration:打开自动配置的总开关
  • @ComponentScan:扫描主类所在包及子包下的 @Component

这里藏着一个容易踩的约定:主类要放在根包。组件扫描默认从主类的包开始往下扫,把主类放到 com.example.util 这种深包里,你的 Service 全都扫不进来。

自己注册 Bean 有两条路:

java
// 路线一:@Configuration + @Bean,常用于第三方类
// 第三方 jar 里的类你改不了源码,加不了 @Component,只能在这声明
@Configuration
public class WebConfig {
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
java
// 路线二:@ConfigurationProperties 批量读配置
@ConfigurationProperties(prefix = "app")
public record AppProps(String name, int timeout) {}

// application.yml:
// app:
//   name: order-service
//   timeout: 3000

@Value 和 @ConfigurationProperties 怎么选:读一两个零散值用 @Value 够了;同一个前缀下有一组配置,或者配置项超过三个,用 @ConfigurationProperties。后者有三个实际好处:松散绑定(yml 里写 app-name 也能映射到 appName 字段)、把一组配置收成一个对象而不是散装字段、配合 configuration-processor 能生成 IDE 自动提示。团队里统一用 @ConfigurationProperties 更省心,配置的读法只剩一种,review 时不用猜某个魔法字符串是哪来的。

配置体系:application.yml 与多环境

Boot 默认读 classpath 下的 application.yml(或 .properties)。多环境靠 profile 文件切:

yaml
# application.yml(公共配置)
spring:
  application:
    name: order-service
  profiles:
    active: dev   # 默认激活 dev 环境

# application-dev.yml  -> 本地:连本机库、开 debug 日志
# application-prod.yml -> 生产:连线上库、只留必要日志

启动时用 --spring.profiles.active=prod 覆盖默认值,同一份包跑不同环境。

比"怎么配"更重要的是谁覆盖谁。同一配置项出现在多个来源时,按优先级排:

  1. 命令行参数(--app.timeout=5000
  2. 操作系统环境变量(APP_TIMEOUT
  3. application-{profile}.yml
  4. application.yml

这个顺序在排查"配置为什么不生效"时直接决定排查方向:先查启动命令和环境变量,再查文件。生产上真实出过这种事:环境变量里残留一个旧值,yml 里改了几遍都不生效,最后发现根本没轮到 yml 说话。完整的排查案例见生产事故排查篇

动手实操:写一个最小 starter

理解 starter 最快的路是自己写一个。目标:做一个 greeting-spring-boot-starter,使用方引依赖、配一行 yml,就能注入 GreetingService。

目录结构:

text
greeting-spring-boot-starter/
├── pom.xml                          # 打成普通 jar
└── src/main/
    ├── java/com/example/greeting/
    │   ├── GreetingService.java
    │   ├── GreetingProperties.java
    │   └── GreetingAutoConfiguration.java
    └── resources/META-INF/spring/
        └── org.springframework.boot.autoconfigure.AutoConfiguration.imports

核心三个类:

java
// GreetingService.java —— 对外提供的能力,故意不加 @Component
// (加不加由自动配置类决定,不由服务类自己决定)
public class GreetingService {
    private final String prefix;

    public GreetingService(String prefix) {
        this.prefix = prefix;
    }

    public String greet(String name) {
        return prefix + ", " + name;
    }
}
java
// GreetingProperties.java —— 把 greeting.prefix 配置收进来
@ConfigurationProperties(prefix = "greeting")
public class GreetingProperties {
    private String prefix = "hello";  // 默认值就是"约定"

    public String getPrefix() { return prefix; }
    public void setPrefix(String prefix) { this.prefix = prefix; }
}
java
// GreetingAutoConfiguration.java —— starter 的灵魂
@AutoConfiguration
@ConditionalOnClass(GreetingService.class)
@EnableConfigurationProperties(GreetingProperties.class)
public class GreetingAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean   // 使用者没自己定义时才注册
    GreetingService greetingService(GreetingProperties props) {
        return new GreetingService(props.getPrefix());
    }
}

最后一步,告诉 Boot 去哪找这个配置类。Boot 2.7+ 写在 imports 文件里(老版本是 spring.factories,3.x 只认 imports):

text
# META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
com.example.greeting.GreetingAutoConfiguration

使用方 mvn install 这个 starter 之后:

yaml
# 使用方的 application.yml
greeting:
  prefix: 你好
java
@Autowired
GreetingService greetingService;

greetingService.greet("祥哥");  // -> "你好, 祥哥"

回头看 @ConditionalOnMissingBean 那一行,它就是 Boot 的设计态度:约定给默认值,但永远留覆盖的门。使用者自己定义一个 GreetingService Bean,starter 的默认实现自动让位,不冲突、不报错。

常见误区与小结

  • 把 starter 当成特殊 jar 格式。它就是普通 Maven jar,特殊之处只在 META-INF 里那份 imports 声明和配好的依赖组合。
  • 主类放在深包里导致 Bean 扫不进来。@ComponentScan 默认从主类所在包往下扫,主类放根包。
  • @Value 和 @ConfigurationProperties 混用同一组配置。@Value 没有松散绑定,@Value("${app-name}") 读不到 app.name,两套规则别混。
  • 在 profile 专属文件里设 spring.profiles.active。active 只在主文件、命令行、环境变量里生效,profile 文件里写了也不会被理会。

这一篇把"为什么有 Boot、starter 怎么运作、配置怎么组织"过了一遍,是 Spring 模块的起跑线。下一篇进容器内部看启动过程:注解和配置类怎么变成容器里的 BeanDefinition 登记记录。

参考

  • Spring Boot 官方文档 - Using Spring Boot / Developing Auto-configuration
  • 本地 Maven 仓库翻 spring-boot-starter-web 的 pom,比任何转述文章都直观

手撕 → 框架 → 生产化,一步步把 AI Agent 工程化搞透。
粤ICP备2026104257号-1