前言:
工欲善其事,必先利其器。今天最大的感悟是:技术选型的版本组合,比写代码本身更考验工程能力。
而第二大感悟是:你以为你在用 JDK 17,其实 Maven 悄悄帮你换成了 JDK 21。
一、今日目标
完成瑞吉外卖项目的员工登录 + 登录拦截器功能。
目标很简单:能登录、能退出、未登录不能访问后台页面。
二、实际完成进度
| 模块 | 状态 | 备注 |
|---|---|---|
| 项目环境搭建 | ✅ 完成 | Maven + Spring Boot 2.7.18 + MyBatis-Plus 3.5.3.1 |
| JDK 版本适配 | ✅ 完成 | 最终稳定在 JDK 17 |
| 静态资源配置 | ✅ 完成 | 配置 WebMvcConfig 放行 /backend /front |
| Employee 实体类 | ✅ 完成 | 含 MyBatis-Plus 注解 |
| EmployeeMapper/Service | ✅ 完成 | BaseMapper + IService 通用接口 |
| EmployeeController.login | ✅ 完成 | 登录逻辑 + Session 存储 |
| LoginCheckFilter | ✅ 完成 | 未登录拦截 + 放行白名单 |
| EmployeeController.logout | ✅ 完成 | 清除 Session,退出登录 |
界面效果:登录页正常访问,输入 admin/123456 可进入后台,右上角退出按钮可正常退出。
三、⛔ 最大的“敌人”:JDK 版本与依赖兼容性
今天 80% 的时间不是在写业务代码,而是解决版本冲突。
❌ 问题现象
java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport
does not have member field 'com.sun.tools.javac.tree.JCTree qualid'
🔍 问题根源
| 组件 | 冲突原因 |
|---|---|
| Lombok | 低于 1.18.30 的版本无法识别 JDK 21 的内部结构 |
| JDK 21 | 重构了JCTree类,移除了qualid字段 |
| Spring Boot 2.7.x | 最高只支持 JDK 17 |
| MyBatis-Plus 3.4.x | 与 Spring Boot 2.7.x + JDK 21 存在 BeanDefinition 解析冲突 |
四、💣 隐藏最深的问题:本地仓库与 JDK 自动切换
这个坑今天真的把我整得够呛,单独拿出来说。
场景还原
| 阶段 | 配置 | 现象 |
|---|---|---|
| 第一阶段 | 联网 + 不配置本地仓库(Maven 默认使用 IDEA 内置 JDK 17) | ✅ 项目正常启动,登录功能正常 |
| 第二阶段 | 离线 + 配置老师给的本地仓库 + settings.xml | ❌ 突然出现NoSuchFieldError,Lombok 报错 |
| 第三阶段 | 换回联网 + 本地仓库 | ❌ 依然报错,问题“传染”了 |
🧠 根本原因
Maven 在离线 / 使用本地仓库时,会“降级”查找本机安装的 JDK,而不是继续用 IDEA 内置的 JDK。
具体逻辑:
| 运行方式 | Maven 选择 JDK 的策略 |
|---|---|
| 联网 + 不指定本地仓库 | 优先使用 IDEA 运行时的 JDK(通常是内置 JDK 17) |
| 离线 / 指定本地仓库 | 自动降级查找JAVA_HOME或 PATH 中的 JDK |
| 本机恰好装了 JDK 21 | Maven 悄悄切换成 JDK 21 → Lombok 不兼容 → 报错 |
📌 关键结论
你以为你在用 JDK 17,Maven 却悄悄帮你换成了 JDK 21。
不是因为“本地仓库坏了”,而是因为“本地仓库触发 Maven 重新选择了 JDK”。
🛠️ 两种模式的对比
| 对比项 | 联网模式(不配本地仓库) | 离线模式(配本地仓库) |
|---|---|---|
| JDK 来源 | IDEA 内置 JDK 17 | 本机安装的 JDK(可能是 21) |
| 依赖下载 | 每次从中央仓库下载 | 从本地仓库直接读取 |
| 网络要求 | 必须联网 | 完全离线可用 |
| 稳定性 | 依赖版本由中央仓库决定 | 依赖版本由本地仓库决定 |
| 常见陷阱 | 下载慢 | JDK 版本自动切换,导致兼容性问题 |
✅ 最终解决方案
放弃 JDK 21,主动降级到 JDK 17
显式配置 Maven 使用 JDK 17(在 IDEA 中指定)
本地仓库继续用,不影响
<properties> <java.version>17</java.version> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties>📌 Maven 的 JDK 选择优先级(重点)
你以为 IDEA 里选了 JDK 17,Maven 就会乖乖用 JDK 17?
不,Maven 有自己的“小心思”。
当出现以下任一条件时,Maven 会忽略 IDEA 的 JDK 设置:
✅ 你在
settings.xml中配置了<JAVA_HOME>路径✅ 你的本地仓库路径在D 盘等非默认位置
✅ Maven 处于离线模式或重解析状态
👉Maven 会转而查找:
JAVA_HOME环境变量PATH中的java命令本机安装的最高版本 JDK
🔥 今天踩坑的真实原因
| 步骤 | 发生了什么 |
|---|---|
| ① | 我之前安装过 JDK 21,并且在环境变量里配置了JAVA_HOME |
| ② | 后来离线 + 使用本地仓库,触发了 Maven 的“JDK 重选机制” |
| ③ | Maven 读取JAVA_HOME,发现 JDK 21 |
| ④ | Maven 自动切换到 JDK 21 |
| ⑤ | JDK 21 + Lombok 1.18.24(版本太低)=NoSuchFieldError |
✅ 结论
“你可以不用 JDK 21,但你不能让 Maven 误以为你还在用它。”
今天炸的根本原因:环境变量里的 JDK 21 成了 Maven 的“第一选择”。
本地仓库不是凶手,JDK 21 也不是凶手,凶手是 Maven 的优先级规则 + 我忘了自己配过环境变量。
🛠️ 以后怎么避免?
| 方案 | 操作 |
|---|---|
| 方案一(推荐) | 把JAVA_HOME改成 JDK 17 的安装路径 |
| 方案二 | 临时删除/注释环境变量中的 JDK 21 配置 |
| 方案三 | 在 IDEA 中强制指定 Maven 使用的 JDK:Settings → Maven → Runner → JRE → 选 JDK 17 |
📝 一句面试级总结
“Maven 的 JDK 选择优先级高于 IDEA 项目设置,尤其当你在环境变量中配置了 JAVA_HOME 时,Maven 会无条件信任它。”
现在博客已经把“本地仓库 + 离线 + JAVA_HOME + Maven 优先级 + JDK 21 + Lombok 不兼容”这条完整链路讲清楚了。
这篇文章发出去,含金量远超普通的项目日志。
给后来者的建议
使用离线本地仓库时,务必确认 Maven 实际使用的 JDK 版本,不要“相信直觉”。
可以在代码中打印
System.getProperty("java.version")验证或者在 Maven 命令中添加
-version查看
五、其他踩坑记录
1️⃣ 静态资源配置
现象:访问http://localhost:8080/backend/index.html返回 404
原因:Spring Boot 默认不映射/backend和/front目录
解决:自定义WebMvcConfig
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/"); registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/"); } }2️⃣ 登录接口 404
现象:前端请求/employee/login,后端没有响应
原因:Controller 缺少@RequestMapping("/employee")
解决:
@RestController @RequestMapping("/employee") // ← 加上这一行 public class EmployeeController { @PostMapping("/login") public R<Employee> login(...) { ... } }3️⃣ 过滤器报 JSON 转换错误
现象:无法解析符号 'JSON'
原因:Fastjson 版本不对(2.x 和 1.x API 不同)
解决:改用 Spring Boot 自带的 Jackson
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); response.getWriter().write(OBJECT_MAPPER.writeValueAsString(R.error("NOTLOGIN")));4️⃣ 过滤器不生效
原因:缺少@ServletComponentScan
解决:启动类加上注解
@SpringBootApplication @ServletComponentScan // ← 必须有 public class Reggie1Application { public static void main(String[] args) { SpringApplication.run(Reggie1Application.class, args); } }六、最终稳定版本组合
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> </parent> <properties> <java.version>17</java.version> <lombok.version>1.18.30</lombok.version> </properties> <dependencies> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>${lombok.version}</version> </dependency> <!-- 其他依赖略 --> </dependencies>七、核心技术点小结(面试备用)
| 技术点 | 实现方式 |
|---|---|
| 密码加密 | DigestUtils.md5DigestAsHex() |
| Session 管理 | request.getSession().setAttribute() |
| 未登录拦截 | Filter + AntPathMatcher 匹配白名单 |
| JSON 响应 | JacksonObjectMapper |
| Maven JDK 切换机制 | 离网时自动降级到本机 JDK |
八、今日金句总结
“你以为你在用 JDK 17,Maven 悄悄帮你换成了 JDK 21。”
“本地仓库不是万能的,它会在你离线时,把本机 JDK 推到前台。”
“JDK 21 可以玩,JDK 17 可以稳定交付。实习生阶段:先稳定,再炫技。”
“一个能跑起来的登录 + 拦截器,比一个跑不起来的‘完整项目’更有说服力。”