JDK 8 到 JDK 17 迁移不可仅靠更换 JAVA_HOME,因跨越9个主版本,API 移除、模块收紧、包名重构(如 javax→jakarta)导致编译报错、运行时 IllegalAccessError、依赖链断裂及 Spring Boot 版本锁死等问题必然出现,需系统性修复。

可行,但不能只换 JAVA_HOME 就跑起来。JDK 8 到 JDK 17 跨越了 9 个主版本(8→9→10→…→17),中间移除了大量 API、收紧了模块访问、重构了包命名空间,**直接编译/运行几乎必然失败**。
关键不是“能不能升”,而是“哪些地方会立刻报错、怎么绕过或修复”。
编译阶段就卡住:javac 拒绝旧语法和废弃 API
JDK 17 的 javac 默认不认 JDK 8 里习以为常的写法:
-
javax.*包在编译期直接报错(如javax.servlet.http.HttpServlet),必须替换成jakarta.servlet.http.HttpServlet -
sun.misc.BASE64Encoder、com.sun.net.httpserver等内部 API 彻底不可用,编译失败 - Maven 项目若未显式设
,可能误用低版本字节码目标,导致17 UnsupportedClassVersionError
启动时抛 IllegalAccessError:模块系统拦住了反射
哪怕代码编译通过,运行时大概率在 Spring、Hibernate 或自定义反射逻辑处崩溃,典型错误:
java.lang.IllegalAccessError: class xxx cannot access class sun.misc.Unsafe
这是因为 JDK 9+ 引入模块系统后,默认禁止跨模块反射访问非开放类。常见触发点:
- Spring AOP 使用
sun.misc.Unsafe创建代理(老版本 CGLIB) - MyBatis 3.4.x 反射读取
java.lang.Class内部字段 - 任何手动调用
setAccessible(true)访问java.base或java.desktop包内私有成员
临时解法是加 JVM 参数:--add-opens java.base/java.lang=ALL-UNNAMED(按实际报错类补全),但只是掩耳盗铃——长期必须升级对应库或重写逻辑。
依赖链断裂:老库在 JDK 17 下根本加载不了
不是所有“能下载到的 jar”都真能在 JDK 17 上活下来。这些组合基本等于拒绝服务:
-
Log4j 1.x:依赖javax.management旧路径,且无模块声明,启动即NoClassDefFoundError -
Druid 1.1.23及更早:使用com.sun.jndi内部类,被模块系统拦截 -
FastJSON 1.2.83:序列化器硬编码调用sun.misc.Unsafe,运行时报InaccessibleObjectException -
ZooKeeper 3.4.14:NIO 相关反射在 JDK 17 下失效,连接建立失败
必须查清每个依赖的兼容性声明,不能只看“Maven 中央库有这个版本”。例如 Druid 要 ≥ 1.2.16,FastJSON 必须切到 2.x(API 不兼容,得重写序列化逻辑)。
Spring Boot 版本锁死:不升框架,JDK 升了也白搭
Spring Boot 2.x 全系列(包括 2.7.x)官方不支持 JDK 17。即使你强行用 mvn compile 过了,运行时会遇到:
- Spring MVC 初始化失败(
jakarta.servlet类找不到) - Actuator 端点 404(Servlet 容器注册逻辑与 Jakarta EE 9 不匹配)
- 配置绑定异常(
@ConfigurationProperties在新模块约束下解析失败)
必须同步升级到 Spring Boot 3.x(最低 3.0.0),而它又强制要求:
-
spring-boot-starter-web→ 自动拉取jakarta.servlet实现 - Hibernate 6.x(5.6.x 是分水岭,5.4.x 直接启动报错)
- Lombok ≥ 1.18.24(旧版注解处理器不识别 JDK 17 模块信息)
真实迁移中,最易被忽略的是:**JVM 参数和构建工具本身也得升级**。比如 Maven 3.6.3 无法正确处理 JDK 17 的 --release 17,必须 ≥ 3.8.1;Gradle 6.x 对 java-library 插件的模块路径支持有缺陷,推荐用 Gradle 7.6+。这些细节不处理,连第一行日志都打不出来。











