《Spring Boot 入门》7.1 Starter 的组成

从一行 spring-boot-starter-webmvc 出发,拆开一个 starter 的两块拼图——依赖聚合模块与自动配置模块,用 4.1.1 的真实依赖树讲清它到底拉了哪些 jar,再给出 4.0 模块化后的命名三段式、-test 测试模块约定、为什么 starter 不能带 @ComponentScan,以及按用途分类的官方 starter 一览表。

本节目标:搞清一个 starter 由哪两部分组成,拆开 spring-boot-starter-webmvc 看清它拉了哪些依赖,并掌握 4.0 模块化后的命名约定。
适用版本:Spring Boot 4.1.x(Java 21)

7.1 Starter 的组成

第 3 章我们往 pom.xml 里加了这么一行,然后一个能跑 HTTP 的服务就出现了:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>

没有人写 Tomcat 的启动代码,没有人注册 DispatcherServlet,也没有人配置 JSON 序列化。这一行依赖是怎么做到「一引就全到位」的?本章的三节顺着一条线走:先读懂官方 starter(本节),再自己造一个(7.2),最后学会在它不生效时怎么查(7.3)。

7.1.1 一个 starter 的两块拼图

把「starter」这个词拆开,它其实是两种东西的组合:

拼图职责典型内容
依赖聚合模块把「用某技术所需的一组库」收成一个坐标若干 <dependency>,本身几乎没有代码
自动配置模块提供「这些库在什么条件下、装配成什么 bean」@AutoConfiguration 类、@ConfigurationProperties 类、.imports 清单

一个小 starter 可以把两块合并进同一个 jar(7.2 我们就是这么做的);官方的大型 starter 通常拆成两个甚至多个 Maven 模块,让「依赖」和「逻辑」各自独立演进。

判断一个坐标属于哪一块,有个很实用的经验:打开它的 jar,如果里面几乎只有 META-INF,那是依赖聚合模块;如果有 @AutoConfiguration 类,那是自动配置模块。

7.1.2 拆开 spring-boot-starter-webmvc

先看依赖聚合这一块。下面是 4.1.1 版本里 spring-boot-starter-webmvc 的真实依赖树(内容来自该版本的 pom,可以直接在本地仓库里核对):

spring-boot-starter-webmvc 4.1.1
├── spring-boot-starter-jackson
│   ├── spring-boot-starter
│   └── spring-boot-jackson
├── spring-boot-starter-tomcat
│   ├── spring-boot-starter
│   ├── spring-boot-starter-tomcat-runtime
│   └── spring-boot-tomcat
├── spring-boot-http-converter
└── spring-boot-webmvc
    ├── spring-boot-servlet
    ├── org.springframework:spring-web 7.0.9
    ├── org.springframework:spring-webmvc 7.0.9
    └── spring-boot-http-converter (runtime)

再把最底层的 spring-boot-starter 也拆开——它是所有 starter 的公共基座:

spring-boot-starter 4.1.1
├── spring-boot-starter-logging     # Logback + SLF4J 桥接
├── spring-boot-autoconfigure       # 自动配置骨架与条件注解
├── jakarta.annotation-api
└── org.yaml:snakeyaml              # 解析 application.yml

看懂这棵树,几个平时「想当然」的问题就有答案了:

  • 内嵌 Tomcat 从哪来? 从 spring-boot-starter-tomcat 传递进来,不是 spring-boot-starter-webmvc 直接写的。
  • JSON 支持从哪来? 从 spring-boot-starter-jackson。4.x 用 Jackson 3,包名已从 com.fasterxml.jackson 迁到 tools.jackson。
  • 为什么能读 YAML? 因为基座 spring-boot-starter 里有 snakeyaml。
  • 为什么 starter 里几乎没有自己的类? 因为它就是个「依赖清单」,逻辑都在 spring-boot-webmvc、spring-boot-tomcat 这些技术模块里。

顺带确认一个易混点:spring-boot-starter-web 在 4.1.1 里仍然存在,但它的 pom 描述已经写明「deprecated in favor of spring-boot-starter-webmvc」。新项目一律用 spring-boot-starter-webmvc。

7.1.3 为什么拆成「依赖聚合 + 自动配置」

把两块分开,是为了让它们各自解决一个正交的问题:

  • 依赖聚合解决「版本」:spring-boot-starter-webmvc 不写任何 <version>,版本由 spring-boot-dependencies 这份 BOM 统一钉死。你引一行,就等于接受了「这一版 Boot 认可的、彼此兼容的一组版本」。
  • 自动配置解决「装配」:spring-boot-webmvc 里带着 @AutoConfiguration 类,在 classpath 上发现对应条件满足时,把 DispatcherServlet、RequestMappingHandlerMapping 这些 bean 注册好。

如果只有依赖聚合,你引完还得自己写一堆 @Bean;如果只有自动配置,你又得手工对齐每个库的版本。两块拼在一起,才是「引一行就可用」的完整体验。

7.1.4 4.0 模块化后的命名三段式

4.0 最大的破坏性变更就是模块化:原来集中在 spring-boot-autoconfigure 一个模块里的自动配置,被拆散到各技术自己的模块里。拆分遵循一条整齐的命名规则,记住这三段,遇到任何官方 starter 都能反推它的包和模块:

角色命名规则TomcatJackson
技术模块spring-boot-<tech>spring-boot-tomcatspring-boot-jackson
根包org.springframework.boot.<tech>org.springframework.boot.tomcatorg.springframework.boot.jackson
自动配置子包org.springframework.boot.<tech>.autoconfigure...tomcat.autoconfigure...jackson.autoconfigure
Starterspring-boot-starter-<tech>spring-boot-starter-tomcatspring-boot-starter-jackson
测试模块spring-boot-<tech>-test——

这个规律在启动日志里能直接看到证据——Tomcat 相关日志的类名,3.x 是 o.s.b.w.embedded.tomcat.TomcatWebServer,4.x 变成了 o.s.boot.tomcat.TomcatWebServer:

2026-10-09T15:42:07.027+08:00  INFO 43496 --- [           main] o.s.boot.tomcat.TomcatWebServer          : Tomcat initialized with port 8080 (http)
2026-10-09T15:42:09.015+08:00  INFO 43496 --- [tomcat-shutdown] o.s.boot.tomcat.GracefulShutdown         : Graceful shutdown complete

同一套规律还带来一批 starter 改名。旧名在 4.1.1 里大多还留着(能在 BOM 里搜到),但已废弃,正文与新项目一律用新名:

废弃旧名4.x 新名
spring-boot-starter-webspring-boot-starter-webmvc
spring-boot-starter-aopspring-boot-starter-aspectj
spring-boot-starter-oauth2-clientspring-boot-starter-security-oauth2-client
spring-boot-starter-oauth2-resource-serverspring-boot-starter-security-oauth2-resource-server
spring-boot-starter-oauth2-authorization-serverspring-boot-starter-security-oauth2-authorization-server
spring-boot-starter-web-servicesspring-boot-starter-webservices
spring-boot-starter-tomcat(war 部署场景)spring-boot-starter-tomcat-runtime

迁移期如果不想逐个改坐标,可以先整体切到过渡用的 spring-boot-starter-classic / spring-boot-starter-test-classic,它们聚合了旧命名的那套依赖。

7.1.5 -test 测试模块的约定

除了 spring-boot-starter-test 这个通用测试 starter,4.x 还给每个技术配了专属测试模块,命名规则同样是三段式的延伸:

模块   spring-boot-<tech>-test
包     org.springframework.boot.<tech>.test
starter spring-boot-starter-<tech>-test

在 BOM 里能看到大量实例:spring-boot-starter-webmvc-test、spring-boot-starter-jackson-test、spring-boot-starter-data-jpa-test、spring-boot-starter-security-test。它们装的是这个技术专用的测试支持——比如对应的测试切片注解、断言工具、测试用配置。写 Web 层测试时,除了通用测试依赖,往往还要引 spring-boot-starter-webmvc-test 才能用上该层专属的测试设施(第 17 章会具体用)。

7.1.6 为什么 starter 不该带 @ComponentScan

这是自定义 starter 时最容易犯、也最难查的错。看下面这个反面教材:

// 反面教材:不要这样写
@AutoConfiguration
@ComponentScan("com.acme")
public class AcmeAutoConfiguration {
    // ...
}

问题出在 @ComponentScan 的默认语义:它扫描的是「声明它的那个类所在的包及其子包」。上面这行如果落在 com.acme.spring.boot.autoconfigure,默认会去扫 com.acme.spring.boot.autoconfigure.**;而一旦你把包根写成 com.acme(或某个与应用重叠的命名空间),它就会顺着包树扫进使用方的代码里,把本该由应用自己管理的 @Component、@Configuration 一起拉进容器。

后果是:组件被重复注册、应用自己的扫描策略被打乱、某些 bean 的出现变得「看包名运气」。starter 的设计哲学是只装配自己声明的东西,绝不主动扫描别人的代码。正确做法只有三种:

想做的事正确手段
注册一个 bean在 @AutoConfiguration 类里写 @Bean 方法
引入另一个配置类@Import(SomeConfiguration.class)
绑定一组属性@EnableConfigurationProperties(AcmeProperties.class)

记住一句话:starter 里出现的 @ComponentScan,几乎都是 bug。

7.1.7 官方 starter 一览表(按用途分类)

4.1.1 的 BOM 里能搜到 170 个以 spring-boot-starter 开头的坐标(含 -test 变体)。按用途归类,常用的如下:

Web 与视图

Starter用途
spring-boot-starter-webmvcSpring MVC + 内嵌 Tomcat(默认 Web 选择)
spring-boot-starter-webflux响应式 Web
spring-boot-starter-webclient / -restclient声明式 / 命令式 HTTP 客户端
spring-boot-starter-websocketWebSocket
spring-boot-starter-webservicesSpring Web Services(SOAP)
spring-boot-starter-thymeleaf / -freemarker / -mustache服务端模板引擎

数据访问

Starter用途
spring-boot-starter-jdbc原生 JDBC + HikariCP
spring-boot-starter-data-jpaJPA / Hibernate
spring-boot-starter-data-jdbcSpring Data JDBC
spring-boot-starter-data-mongodbMongoDB
spring-boot-starter-data-redisRedis
spring-boot-starter-data-elasticsearchElasticsearch
spring-boot-starter-jooqjOOQ

安全

Starter用途
spring-boot-starter-securitySpring Security 核心
spring-boot-starter-security-oauth2-clientOAuth2 客户端
spring-boot-starter-security-oauth2-resource-serverOAuth2 资源服务器
spring-boot-starter-security-oauth2-authorization-server授权服务器

消息与集成

Starter用途
spring-boot-starter-amqpRabbitMQ / AMQP
spring-boot-starter-kafkaKafka
spring-boot-starter-jms / -activemq / -artemisJMS 与各家实现
spring-boot-starter-integrationSpring Integration

运维与观测

Starter用途
spring-boot-starter-actuator健康检查、指标、端点
spring-boot-starter-micrometer-metricsMicrometer 指标
spring-boot-starter-opentelemetryOpenTelemetry(4.0 新增)
spring-boot-starter-zipkin链路追踪导出

工具、任务与数据库迁移

Starter用途
spring-boot-starter-validationBean Validation
spring-boot-starter-aspectjAOP(旧名 -aop)
spring-boot-starter-cache缓存抽象
spring-boot-starter-batch批处理
spring-boot-starter-quartz定时任务
spring-boot-starter-flyway / -liquibase数据库迁移(4.0 起必须显式引 starter)
spring-boot-starter-grpc-client / -grpc-serverSpring gRPC(4.1 新增)

注意最后一行里的迁移类:4.0 之前,只要 classpath 上有 Flyway 的库就会被自动配置;4.0 起必须显式引 spring-boot-starter-flyway。这类「以前不用 starter、现在必须加」的变化,是升级时最容易被忽略的一类。

7.1.8 一个常见误解:starter 不是「库」

初学者常把 starter 当成「某个功能的实现库」,于是问出「spring-boot-starter-webmvc 和 spring-webmvc 有什么区别」。答案是它们根本不在一个层级:

  • spring-webmvc(org.springframework:spring-webmvc)是实现,装着 DispatcherServlet 这些真正的代码。
  • spring-boot-starter-webmvc 是入口,它把 spring-webmvc 连同 Tomcat、Jackson、自动配置一起拉进来,并保证版本互相兼容。

所以 starter 本身可以「没有一行 Java 代码」——它的价值在于替你做了正确的依赖决策。理解了这一点,7.2 里我们自己写的 starter,重点也就会放在「选对依赖 + 写对自动配置」,而不是写多少业务代码。

7.1.9 怎么查一个 starter 到底拉了什么

7.1.2 那棵树不是背出来的,是查出来的。以后遇到「引了这个 starter 却出现了意外的库」,用下面任意一种方式复现:

方式一:Maven 依赖树。 在项目根目录执行:

mvn dependency:tree -Dincludes=org.springframework.boot:*

输出会把 org.springframework.boot 组下的所有传递依赖按层级列出来,7.1.2 的结构就是它的简化版。只想看某个 starter 的直接依赖时,把 -Dincludes 换成对应坐标即可。

方式二:IDE 的依赖视图。 IntelliJ IDEA 右侧 Maven 面板里展开 Dependencies,能折叠展开整棵树;比命令行更直观,还能直接看到「某个依赖是被谁引入的」。

方式三:直接读 pom。 每个 starter 的 jar 里都带一份 .pom,用压缩工具打开 META-INF/maven/.../pom.xml 就能看到它的 <dependencies>。这也是本节依赖树的数据来源。

一个典型用途:项目里同时出现了 Logback 和 Log4j2,日志格式诡异。查依赖树发现是某个 starter 传递引入了 spring-boot-starter-logging,于是改用 spring-boot-starter-log4j2 并排除默认日志——这类排查的第一步,永远是先把依赖树看清楚。

小结

  • 一个 starter 由两块拼图组成:依赖聚合模块(选对一组库与版本)与自动配置模块(按条件装配 bean);小 starter 可合并进一个 jar,官方大型 starter 常拆成多个模块。
  • spring-boot-starter-webmvc 的真实依赖是 spring-boot-starter-jackson + spring-boot-starter-tomcat + spring-boot-http-converter + spring-boot-webmvc,所有 starter 的基座是 spring-boot-starter。
  • 4.0 模块化的命名三段式:模块 spring-boot-<tech>、包 org.springframework.boot.<tech>、starter spring-boot-starter-<tech>,测试模块为 spring-boot-<tech>-test。
  • 一批 starter 在 4.x 改名(如 -web → -webmvc、-aop → -aspectj),旧名保留但已废弃。
  • starter 里不能出现 @ComponentScan,它会扫进使用方的包;应改用 @Bean、@Import、@EnableConfigurationProperties。

读懂了官方 starter 的骨架,下一步就是自己动手造一个——这会逼你面对 7.1 里提到的每一个约定:依赖怎么选、自动配置类怎么写、.imports 文件放哪。

阅读导航:上一节:6.3 外部化配置的优先级 · 下一节:7.2 写一个自定义 Starter 。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「java」更多文章

  1. 《Spring Boot 入门》18.3 打包与运行
  2. 《Spring Boot 入门》18.2 实现
  3. 《Spring Boot 入门》18.1 需求与设计