楼层: 首页/ 软件技术/ Spring Boot 3.5/ 常见坑与最佳实践
14

常见坑与最佳实践

Common Pitfalls
坑 1:javax 还是 jakarta

Spring Boot 3.x 全量走 jakarta 命名空间。如果你从 Spring Boot 2.x 升级,所有 javax.persistence.*、javax.servlet.*、javax.validation.* 都要改成 jakarta.*。网上抄到老代码先检查包名。

坑 2:循环依赖

Spring Boot 2.6+ 默认禁止循环依赖,启动直接报错。别用 spring.main.allow-circular-references=true 去放开——那是在给设计缺陷打补丁。正确做法是拆公共逻辑到第三个类。

坑 3:配置改了不生效

① 检查是否有更高优先级的配置覆盖(命令行、环境变量);② 检查 profile 是否真的激活了;③ 检查 YAML 缩进——YAML 对缩进敏感,两个空格和 Tab 混用直接读不到。

坑 4:DevTools 别上生产

spring-boot-devtools 支持 classpath 变化自动重启,开发很爽。但它会监控文件变化,生产环境引入它有安全和性能问题。打 jar 时 DevTools 默认不打包,但手动加了就可能漏,生产依赖里务必检查。

坑 5:静态资源改了不生效

开发时改了 static/ 下的 HTML,浏览器还是老版本?两个原因:① 浏览器缓存,强制刷新(Ctrl+Shift+R);② Spring Boot 对静态资源有缓存,开发时加 spring.web.resources.cache.period=0 关掉。

坑 6:@Transactional 在 Spring Boot 里照样失效

Spring Boot 没改事务的底层规则——同类调用、非 public 方法、异常被 catch、类没被 Spring 管理,这四种场景 @Transactional 一样不生效。跟 Spring 核心一页讲的一样。

Spring Boot 常用注解速查表

注解一句话说明
@SpringBootApplication主启动类标记,三合一。
@RestControllerREST 控制器,方法返回 JSON。
@RequestMapping类级路径前缀。
@GetMapping / @PostMapping / @PutMapping / @DeleteMappingHTTP 方法映射。
@RequestParam取 URL 查询参数。
@PathVariable取路径变量。
@RequestBody取请求体 JSON。
@RequestHeader取请求头。
@RestControllerAdvice全局异常处理 + 全局数据绑定。
@ExceptionHandler声明处理某类异常。
@Valid触发参数校验。
@ConfigurationProperties批量绑定配置。
@Value单个值注入。
@Profile多环境条件装配。
@Scheduled定时任务。
@Async异步方法。
@Transactional声明式事务。
@MockBean测试时替换 Bean 为 Mock。
@WebMvcTest / @DataJpaTest切片测试。

Spring Boot 学习路线(六周计划)

周次目标
第 1-2 周用 Initializr 起项目,写 5 个 REST 接口,理解自动配置。
第 3 周接 H2 + JPA,做 CRUD + 分页 + 校验。
第 4 周写全局异常处理、拦截器、参数校验。
第 5 周接 Actuator,配 Dockerfile,打镜像跑起来。
第 6 周写测试:@WebMvcTest 测 Controller,@DataJpaTest 测 Repository。

从 Spring Boot 2.x 升到 3.x 要改什么

改动项怎么改
JDK8/11 升到 17+。
包名javax.* 全部改成 jakarta.*(IDE 批量替换)。
版本spring-boot-starter-parent 改成 3.5.x。
Hibernate5.x 升到 6.x,JPA 注解包名跟着变 jakarta。
Tomcat9 升到 10.1,Servlet API 也走 jakarta。

Spring Boot 3.5 新特性速览

特性说明
JDK 21 支持虚拟线程(Virtual Thread)开箱即用,spring.threads.virtual.enabled=true 一行开启。
HTTP Interface声明式 HTTP 客户端,写个接口就能调远程 API。
GraalVM 原生镜像支持编译成本地可执行文件,启动速度毫秒级。
ObservibilityMicrometer Observation API 统一日志、指标、链路追踪。
记
Spring Boot 一句话总结

① 约定优于配置:starter 拉依赖、自动装配 Bean,开箱即用。

② 分层写代码:Controller 收参数、Service 写业务、Repository 碰数据库,别串。

③ 校验用 @Valid,异常集中 @RestControllerAdvice,别在 Controller 里 if 来 if 去。

④ 版本红线:Spring Boot 3.5 = JDK 17+ = jakarta.*,别再抄 javax 老代码。

两页教程读完,你已经掌握了 Java 后端最主流的技术栈。接下来要做的事:打开 IDE,把银行转账系统和任务管理 API 各跑一遍。能跑起来、能改、能 debug,这才算真正学会。光看不练,三周就忘;边读边敲,一辈子不忘。

自测题 · Spring Boot

1.(概念题)Spring Boot 说的"约定优于配置",跟传统 Spring 比到底省了什么事?

查看答案

答案:传统 Spring 你要手动配 DispatcherServlet、视图解析器、数据源、事务管理器、视图解析器……一堆 XML 或 Java 配置。Spring Boot 根据类路径下的依赖自动配好这些,你只写业务代码。比如加了 spring-boot-starter-web,Tomcat 和 MVC 自动就绪。解析:底层就是 @ConditionalOnClass 这类条件判断。

2.(选择题)Spring Boot 3.5.x 最低要求哪个 JDK 版本?A. JDK 8 B. JDK 11 C. JDK 17

查看答案

答案:C,JDK 17。解析:Spring Boot 3.x 基于 Spring Framework 6,Spring 6 放弃了 JDK 8/11,最低 17,推荐 21 LTS。

3.(排错题)小李在 Spring Boot 3.5 项目里 import 了 javax.persistence.Entity,启动报 ClassNotFoundException。怎么回事?

查看答案

答案:包名错了。Spring Boot 3.x 用 Jakarta EE 9+ 命名空间,应该是 jakarta.persistence.Entity。解析:javax → jakarta 是 Jakarta EE 9 的硬性变化,Spring Boot 2.7 还能兼容 javax,3.x 彻底切掉。

4.(思考题)为什么 @SpringBootTest 启动慢,而 @WebMvcTest 启动快?

查看答案

答案:@SpringBootTest 全量启动 Spring 容器,连数据库、Redis、MQ 全都初始化;@WebMvcTest 是切片测试,只加载 Spring MVC 相关的 Bean,数据库和 Service 用 @MockBean 替换。解析:测试时优先用切片测试,又快又隔离。

5.(判断题)把 spring-boot-devtools 打到生产 jar 里也没事,反正它默认不生效。对吗?

查看答案

答案:不对。DevTools 会监控 classpath 变化触发重启,生产环境引入它有安全和性能风险。虽然 spring-boot-maven-plugin 默认不把 DevTools 打进可执行 jar,但手动加了就可能漏。生产依赖里务必检查,别图省事。解析:DevTools 是开发期工具,不是运行时依赖。

综合练习题 · 学完整个 Spring Boot 再做

1.(设计题)要做一个"用户登录"接口:接收用户名密码,校验后返回 token。请列出需要哪些层、每层写什么注解。

查看答案

参考:LoginRequest DTO(@NotBlank 校验);LoginController(@RestController/@PostMapping,参数 @Valid @RequestBody);LoginService(@Service,校验密码、生成 token);GlobalExceptionHandler 接住"用户不存在/密码错误"返回 401。加分:密码错误次数记 Redis,超过 5 次锁账号。

2.(排错题)启动报 "Parameter 0 of method ... required a bean of type 'XxxMapper' that could not be found"。可能是什么原因?

查看答案

答案:Mapper 接口没被扫到。常见:① @MapperScan 没配、Mapper 接口上没贴 @Mapper;② Mapper 所在包不在主启动类的扫描包范围内。解析:在主启动类加 @MapperScan("com.example.mapper"),或每个 Mapper 接口贴 @Mapper。

3.(理解题)为什么 Spring Boot 2.6 以后默认禁止循环依赖?怎么排查一个循环依赖?

查看答案

答案:循环依赖是设计缺陷,掩盖了不合理的耦合。报错信息里会列出"A depends on B, B depends on A"。排查:看启动日志的依赖链,把共用逻辑抽到第三个类 C,让 A、B 都依赖 C 而不是互相依赖。解析:别用 allow-circular-references=true 去关掉报错,那是掩耳盗铃。

4.(实现题)给任务列表接口加一个"按标题模糊搜索 + 分页"。Controller 和 Service 分别怎么改?

查看答案

参考:Repository 加 findByTitleContaining(String kw, Pageable p);Service 方法接收 keyword 参数,为空就 findAll(pageable),非空就 findByTitleContaining(kw, pageable);Controller 的 list 方法加 @RequestParam(required=false) String keyword。加分:用 Specification 或 Example 做动态条件。

5.(部署题)本地用 H2 跑得好好的,要部署到生产 PostgreSQL,需要改哪些东西?

查看答案

答案:① pom 把 H2 换成 postgresql 驱动;② application-prod.yml 改 datasource url/username/password;③ 加 Flyway/Liquibase 管表结构,ddl-auto 改 none;④ 生产 profile 关闭 show-sql、开启日志文件输出;⑤ 连接池参数按生产库调。解析:本地 H2 和生产 PG 的 SQL 方言可能有差异,复杂查询要在 PG 上回归一遍。

学习这两个页面的正确姿势:先把 Spring 核心页的 IoC 和事务跑通,再来看 Spring Boot。Spring Boot 帮你省了配置,但底层原理全在 Spring 核心页里。不懂 IoC 就学 Boot,等于不知道房子地基在哪就开始装修——能住,但漏雨的时候不知道找谁修。

最后一句话:Spring Boot 不是魔法,它只是把 Spring 官方认可的最佳实践写成了自动配置类。你看源码时会发现,那些自动配置类里写的东西,跟你手写的 @Configuration 没区别——只是它帮你写好了。理解了这一点,Spring Boot 就没有秘密了。

面试被问"Spring Boot 自动配置原理",标准答案三句话:@SpringBootApplication 包含 @EnableAutoConfiguration;它去读 META-INF 下的 imports 文件;里面列的每个自动配置类都带 @ConditionalOnXxx,满足条件才注册 Bean。能讲到这三层,面试官就知道你真懂。讲不出来,背下来也行——这是必考题。

最后:动手跑起来

教程看十遍不如自己跑一遍。照着第 12 章的任务管理 API,从 Initializr 建项目开始,一行一行敲出来。跑通后加个"完成任务"接口,再加个分页查询。能跑、能改、能 debug,这一页才算真的学会。

遇到报错别慌:把红字复制下来,先看最后一行异常类型,再往上找你的类名——90% 的问题出在你自己写的代码里,不是 Spring 的锅。把错误信息贴给 AI,它能告诉你哪一行写错了。

恭喜你看到这里。两页教程加起来近 3000 行,但真正的学习才刚开始——合上浏览器,打开 IDE,建个项目,写第一个接口。代码跑起来的那一刻,比读十遍教程都管用。祝你在 Java 后端的路上越走越远。