☰
Spring Boot项目依赖怎么选?从starter机制到常用依赖清单全解析
2026/10/22 4:51:31 网站建设 项目流程

新项目一开始,pom.xml里就躺着一长串依赖,这是我给朋友排查项目时最常见的画面。二十几行依赖看起来热闹,但真正创建 Spring Boot 项目时该勾的、不该勾的,其实就那几样。这篇文章我从项目初始化视角出发,把最常见的依赖逐个拆开讲清楚:它解决什么问题、内部帮你带进了什么、哪些坑是创建时就埋下的,以及我反复试错后沉淀下来的依赖清单。无论你用的是 2.7 还是 3.x,思路都通用。

1. 从勾选依赖到项目能跑:starter这套机制就是地基

很多初学者会把依赖理解成"拼积木",在初始化页面上多勾几个心里才踏实。但实际上,Spring Boot 的依赖体系更像一辆整车——你只需要告诉它"我要开去哪",它就把发动机、变速箱、轮胎一次性给你配好。这套机制的核心就是 starter。

1.1 一条starter声明,为什么能把一整车依赖都带进来

我先说个最直观的例子:创建项目时勾了 Spring Web,生成的pom.xml里只多了一行:

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

但如果你在项目里运行mvn dependency:tree,看到的是几十行依赖,包括spring-webmvc、spring-web、spring-boot-starter-json、内置 Tomcat 等。这就是 Maven 的传递依赖机制在起作用:starter 本身几乎不写代码,它只是一个"聚合描述文件",把实现某个功能所需的一整套依赖,在 POM 文件里通过<dependencies>声明好。你引入 starter,等于一次性引入了它底下所有的孩子。

理解这一点之后,你就不会再犯"怕漏了 Tomcat,手动再加一个 Tomcat 依赖"这类错误了。

1.2 Spring Boot版本、parent POM和依赖版本号的三角关系

第二个要搞明白的是版本号。新建项目时你选中的 Spring Boot 版本(比如 2.7.18 或 3.2.x),其实就是整个依赖体系的"总闸"。项目里会默认继承一个叫spring-boot-starter-parent的父 POM,它内部用 Maven 的dependencyManagement把所有常用依赖的推荐版本都锁定了。

所以你在写自己的依赖时,绝大多数都不用写<version>。一旦你手动给某个依赖强加了一个版本号,等于直接跳过了统一管理,搞不好就会和其他依赖版本冲突。

还要提醒一下:Spring Boot 2.x 系列用的是javax.*命名空间,3.x 系列换成了jakarta.*。这意味着同样是加 validation 依赖,2.x 里注解是javax.validation.*,3.x 里必须写jakarta.validation.*。网上老教程里很多代码在 3.x 下直接报错,多半就是这个问题。

2. 创建项目时最常勾的这些starter,各自扛了什么活

下面把我认为"创建项目时最常用"的 starter 逐个拆开。不是让你全勾,而是要知道每个东西进去是干嘛的,后面删起来才不会两眼一抹黑。

2.1 spring-boot-starter-web:项目能跑起来的第一块砖

这是 Web 项目的命脉。它干了三件事:引入 Spring MVC(Controller、RestTemplate 那套)、引入内置 Tomcat、引入 JSON 序列化组件(Jackson),让你能直接写出:

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

然后mvn spring-boot:run就能在 8080 端口收到响应。如果你做的是纯后端 API 服务,这是必选项;如果打算做 Spring Cloud 微服务里的一个基础服务,也几乎绕不开它。

创建时容易漏掉的是"内置 Tomcat 的端口配置"和"上下文路径配置"。这些不需要额外依赖,属于application.yml的配置活,新手常误以为要加什么 server 相关依赖,其实完全不用。

2.2 spring-boot-starter-validation:参数校验的省力方案

如果是接外部请求的接口服务,参数校验几乎是刚需。这个 starter 把 JSR 380(Bean Validation)的标准实现 Hibernate Validator 带了进来,然后在 Spring Boot 里自动配置好了适配。

用法上,DTO 字段加注解,Controller 层加@Valid:

public class UserCreateRequest { @NotBlank(message = "用户名不能为空") private String username; @Email(message = "邮箱格式不正确") private String email; }
@PostMapping("/users") public Result createUser(@Valid @RequestBody UserCreateRequest request) { // 到这里说明参数已通过校验 }

少了这个依赖,你只能在每个接口里手写一堆 if 判断,又臭又长,校验逻辑还容易漏。创建时顺手勾上,后面写接口会轻松很多。

2.3 spring-boot-starter-data-jpa:持久层的主力选手

如果你的业务属于典型的 CRUD 场景,spring-boot-starter-data-jpa就是最常见的持久层选择。它不是单独工作的,内部引入了 Spring Data JPA 和 Hibernate ORM,它会帮你完成实体映射、Repository 接口自动实现等一堆工作。

一个接口就够基础增删改查:

public interface UserRepository extends JpaRepository<User, Long> { List<User> findByUsername(String username); }

需要注意:勾了 JPA 不代表数据库就能跑了。你还得补一个数据库驱动依赖(比如 MySQL 驱动),以及至少一个数据源配置(url、用户名、密码)。很多新手创建时勾了 JPA,运行时报找不到数据源,就是这个环节漏了。

2.4 spring-boot-starter-data-redis:缓存和分布式场景的基础件

项目如果要做缓存、计数器、分布式锁,Redis 基本跑不掉。这个 starter 会引入 Spring Data Redis 和默认的 Lettuce 客户端连接池。

创建时如果你的项目定位是中小型后台系统,可以先勾上,因为后面大概率会用到;如果只是纯算法演示、本地 Demo,那可以先不勾,省得还要本地起一个 Redis 服务才能启动项目。

Redis 的常用场景我列一下,方便判断要不要:

  • 热点数据缓存:接口频繁查询数据库时,先查 Redis。
  • 分布式锁:多个实例同时处理同一任务时,需要用 Redis 锁避免重复执行。
  • 会话共享:分布式部署下 Session 统一存到 Redis。

2.5 spring-boot-starter-test:测试基础设施,一进来就该配齐

这个 starter 很特别,Spring Initializr 创建项目时默认就带上了。它聚合了 JUnit 5、Spring Boot 测试框架、AssertJ 断言库、Mockito 等测试相关工具链。

很多初学者觉得测试代码可有可无,但这个依赖默认带着是有道理的。你写最小启动测试时:

@SpringBootTest class ApplicationTests { @Test void contextLoads() { } }

没有spring-boot-starter-test,这一行上下文加载测试都跑不起来。而且后面你写业务代码时,单元测试、MockMvc 测试都是从这个基础上长出来的。我建议永远保留它,别为了省启动时间把它框掉。

3. 持久层的三岔路口:JPA、MyBatis和JdbcTemplate该选谁

创建项目时,持久层方案是最需要提前想清楚的。因为它直接影响数据访问代码怎么写、表结构怎么做、后期维护成本高不高。下面把我自己的评估逻辑摊开来讲。

3.1 三者的开发效率和底层逻辑差异

为了看得清楚,我把三个方案放在一张表里对比:

方案核心逻辑开发效率SQL 控制力上手难度适用形态
Spring Data JPA面向实体,自动生成 SQL单表 CRUD 极高弱,复杂 SQL 需要写 JPQL 或原生 SQL中单表操作多、表结构简单的业务系统
MyBatis半自动,SQL 由你写复杂 SQL 较高强,完全掌控 SQL中SQL 复杂、多表关联多、报表类查询
JdbcTemplate手动写 SQL,手动映射结果低,代码量大极强低轻量小工具、原型验证、临时脚本

从原理上讲,JPA 是 ORM(对象关系映射),它会根据你的 Repository 方法名自动推断 SQL 该怎么写,开发时舒服,但一旦业务查询绕到多表 join 和动态条件,它生成出来的 SQL 就不太好控制了。MyBatis 反过来,你把 SQL 写在 XML 或注解里,每一句都是自己写的,灵活度高,代价是要手工维护 SQL 文件。JdbcTemplate 则接近 JDBC 的原始形态,所有字段映射都需要自己处理,代码量大,但最简单直接。

3.2 我的选型参考:业务形态决定路线

我现在做选型时会按这三条线来判断:

如果项目是典型的后台管理系统,比如用户管理、菜单管理、角色权限,表结构相对规整,单表 CRUD 比例高,我会选 Spring Data JPA。它能帮我把大部分简单查询直接省掉,而且实体关系映射写起来很自然。

如果项目是报表统计类、计费类、复杂电商交易类的,SQL 动不动就是多表 join、子查询、动态条件,我会首选用 MyBatis。因为这类需求的性能调优本质上就是 SQL 调优,把 SQL 牢牢攥在自己手里才有保障。

如果项目只是一个快速原型、临时数据分析工具,连前端页面都不打算做,我会直接用 JdbcTemplate,免去搭建 ORM 配置环境的成本,几分钟就能把查询跑起来。

3.3 数据库驱动与连接池:缺了这步,写再好的代码也白搭

选完持久层方案,还必须配上数据库驱动。以最常见的 MySQL 为例,用 JPA 或 JdbcTemplate 时都要加:

<dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <scope>runtime</scope> </dependency>

注意 artifactId 在老版本里是mysql-connector-java,8.0.31 之后统一改成mysql-connector-j。如果你拿老教程里的坐标直接粘贴,到新版本里会提示找不到依赖。

连接池方面,Spring Boot 2.0 之后默认使用 HikariCP,性能好、配置简单,基本不需要额外引入连接池依赖。如果你项目里真的需要监控 SQL 执行情况,可以再考虑引入其他连接池,但那属于锦上添花,不是创建项目时就必须加的。

数据源配置长这样:

spring: datasource: url: jdbc:mysql://localhost:3306/test_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver

如果创建时忘了配驱动,项目启动时会报Failed to configure a DataSource,原因就是 Spring Boot 检测到了持久层相关 starter,但没找到可用的数据源。

4. 加依赖之前先算账:传递依赖、版本冲突与那些容易后悔的勾选

依赖这东西,勾的时候很痛快,跑起来才慢慢还债。我见过太多项目,光依赖冲突就排查了一整天。下面把最常见的几类问题提前摆出来。

4.1 依赖冲突的症状和快速定位手段

典型症状包括两大类:一类是启动时直接报NoSuchMethodError或ClassNotFoundException,另一类是运行时行为诡异,比如 JSON 序列化结果和预期不一致、某个注解不生效。

遇到这种情况,第一步不是百度报错信息,而是先看依赖树。我用的最多的命令是:

mvn dependency:tree

它会输出依赖树的完整网络图。比如你在使用 Redis 时发现 Lettuce 版本不对,可以直接精确过滤:

mvn dependency:tree -Dincludes=io.lettuce:lettuce-core

然后顺着树往上找,看是哪个依赖传递进来的。定位到源头之后,再用<exclusions>把不该进来的版本排掉,或者用父 POM 里的<properties>覆盖版本号。这里有个关键心得:排依赖和改版本要"动最少的地方",不要为了修一个冲突,把一个模块的依赖全删了重写。

4.2 手动覆盖版本号的时机与代价

大多数情况下我强烈反对手动给依赖写死版本号。Spring Boot 的dependencyManagement已经针对大量主流依赖做了兼容性测试。你一旦手动写版本,就打破了这套保障。

但有两种例外我会手动覆盖:一是公司内部自研的 SDK,groupId 和 artifactId 是内部的,Spring Boot 不认识;二是某个第三方库需要更高版本才能修复一个关键 Bug,我会在<properties>中只覆盖那个库的版本号,而不是直接在<dependency>里硬写。

覆盖的代价是:你必须重新跑一遍完整测试,确认和 Spring Boot 的其它组件没有隐性冲突。千万别覆盖完版本号、启动没问题了就直接上线,这种坑我踩过不止一次。

4.3 Lombok和devtools这两兄弟的位置

创建项目时,Lombok 和 Spring Boot DevTools 也是很多人爱勾的项目。

Lombok 不是一个 starter,它是编译期注解处理器。它带来的问题是:如果你的团队成员 IDE 没有装对应的插件,代码就会因为找不到 getter/setter 方法报编译错误。它不参与运行期,只是帮你省写代码。项目如果团队协作人数多,要不要引入 Lombok 必须提前达成一致,否则后患无穷。

DevTools 主要是开发时热重启。优点是小改动不用手动重启服务,缺点是它改了类加载机制,偶尔会产生一些"重启后状态不对"的奇怪问题。我在做纯 API 调试时会保留它,做定时任务或消息消费相关的模块时通常会去掉,因为重启行为会干扰长驻进程。

最后一个创建时常见的后悔项是:把只在某个模块用到的依赖在项目创建时勾满。比如用不上消息队列的模块,先把 AMQP 相关 starter 勾进去了,启动时会去连一个不存在的消息中间件,白白拖慢启动速度,还要额外配置 exclude。原则很简单:用到再加,不用就不加。

5. 我每次创建Spring Boot项目时的依赖检查清单

讲完原理和坑,直接给结论。下面是我在实际项目创建时用的检查清单,你可以直接照着抄。

5.1 一个业务Web服务的最小依赖集合

做一个规规矩矩的后台 API 服务,我的最小集合如下:

目的依赖备注
Web 接口spring-boot-starter-web含 Tomcat、Spring MVC、Jackson
参数校验spring-boot-starter-validation建议开发态就加,不然后面补很麻烦
持久层spring-boot-starter-data-jpa或mybatis-spring-boot-starter根据业务形态二选一
MySQL 驱动mysql-connector-j记得 scope 为 runtime
测试spring-boot-starter-test默认就带,别删
编译省力lombok团队一致同意再加
开发热重载spring-boot-devtools可选,做复杂长驻任务时不建议

这一套下来,足够支撑一个可以部署上线的中小型 Web 服务了。在这个基础上再加其他依赖,思路也是先问一句:这个功能现在就要用吗?不是的话,标记成 TODO,等需要时再加。

5.2 不同场景下的依赖追加规则

根据项目类型,我会额外追加不同的依赖。

做缓存、分布式锁、Session 共享时,加spring-boot-starter-data-redis。做接口文档展示时,加 springdoc-openapi 那套依赖,但注意它的版本必须和 Spring Boot 版本匹配,否则启动直接报 null 指针。做消息推送、异步解耦时,按实际用的中间件加对应的 starter 或 SDK。

做微服务全家桶时,spring-cloud 系列的依赖(服务注册发现、配置中心、网关)是另一套版本管理体系。它不能只靠 Spring Boot 版本锁版本,还得声明对应的 Spring Cloud 版本,创建时一定要看清兼容关系。

5.3 创建时多勾、创建后清理的常见后悔药

如果创建时真的勾多了,也不用慌,删除依赖时守住一条原则:每次只删一个,删完先启动和跑测试,再删下一个。一次性删五六个依赖,出了问题根本不知道是谁引起的。

需要特别注意的是,有些依赖表面上只加了一行,实际牵连很广。比如你不小心加了一个不用的消息中间件 starter,删除后还要检查application.yml里有没有残留的 broker 地址配置,否则配置里的地址会被 Spring Boot 尝试连接,白白消耗启动时间。

最后再补一句实在话:项目的依赖治理,功夫在创建项目之前。开头多想五分钟,后面就能少排查五小时。每次创建新项目时,我都会把这份清单过一遍,根据是否要用到、什么时候用到来判断该勾什么,这样项目从一开始就是干净的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询