从 JDK 8 或 JDK 11 升级到 JDK 17,鹏磊最烦的就是各种兼容性问题。代码编译不过,运行时出错,第三方库不兼容,一堆问题。这篇文章就给你整一个完整的迁移指南,帮你把应用从 JDK 8/11 平滑升级到 JDK 17。
从 JDK 8/11 升级到 JDK 17 是个大工程,需要检查代码兼容性、更新第三方库、调整配置参数。这玩意儿虽然工作量不小,但 JDK 17 是 LTS 版本,性能和安全都有提升,值得升级。建议制定详细的迁移计划,逐步执行,充分测试,确保平滑升级。
迁移前准备
迁移前需要做好准备工作:
检查当前环境
检查当前 JDK 版本和依赖:
# 检查当前 JDK 版本
java -version
# 检查编译版本
javac -version
# 检查项目依赖
mvn dependency:tree
# 或
gradle dependencies
检查当前环境能帮你了解迁移的起点。
使用 jdeps 检查依赖
使用 jdeps 工具检查 JDK 内部 API 依赖:
# 检查 JDK 内部 API 依赖
jdeps --jdk-internals MyApp.jar
# 检查模块依赖
jdeps --list-deps MyApp.jar
# 生成依赖报告
jdeps -dotoutput deps MyApp.jar
使用 jdeps 工具能帮你找出所有依赖内部 API 的地方。
使用 jdeprscan 检查废弃 API
使用 jdeprscan 工具检查废弃 API:
# 检查废弃 API
jdeprscan MyApp.jar
# 检查特定版本
jdeprscan --release 17 MyApp.jar
使用 jdeprscan 工具能帮你找出所有使用废弃 API 的地方。
从 JDK 8 升级
从 JDK 8 升级到 JDK 17,需要注意:
模块系统
JDK 9 引入了模块系统,JDK 17 继续使用:
// JDK 8:不需要模块声明
// 可以直接使用所有类
// JDK 17:可能需要模块声明
module my.module {
requires java.base;
requires java.desktop;
exports com.example;
}
如果项目没有模块化,可以继续使用类路径,但建议逐步模块化。
内部 API 封装
JDK 17 强封装了内部 API:
// JDK 8:可以访问内部 API
// import sun.misc.Unsafe; // 可以访问
// JDK 17:无法访问内部 API
// import sun.misc.Unsafe; // 无法访问
// 需要使用公开 API 替代
需要将内部 API 迁移到公开 API。
废弃 API
JDK 8 到 JDK 17 之间废弃了很多 API:
// JDK 8:使用某些 API
// JDK 17:这些 API 可能被废弃或移除
// 需要使用替代方案
需要检查并更新废弃的 API。
从 JDK 11 升级
从 JDK 11 升级到 JDK 17,相对简单一些:
--illegal-access 选项移除
--illegal-access 选项在 JDK 17 中被移除:
# JDK 11:可以使用 --illegal-access
java --illegal-access=permit MyApp
# JDK 17:--illegal-access 被移除
java --illegal-access=permit MyApp # 错误:未知选项
需要移除 --illegal-access 选项,使用公开 API。
内部 API 完全封装
JDK 17 完全封装了内部 API:
// JDK 11:可能还能访问某些内部 API(有警告)
// JDK 17:完全无法访问
// 需要使用公开 API 替代
需要将内部 API 迁移到公开 API。
迁移步骤
步骤一:更新构建工具
更新构建工具到支持 JDK 17 的版本:
<!-- Maven 配置 -->
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
// Gradle 配置
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
更新构建工具确保能正确编译。
步骤二:更新第三方库
更新第三方库到支持 JDK 17 的版本:
<!-- 检查库是否支持 JDK 17 -->
<!-- 更新到最新版本 -->
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>my-library</artifactId>
<version>2.0.0</version> <!-- 更新版本 -->
</dependency>
</dependencies>
更新第三方库确保兼容性。
步骤三:修复编译错误
修复编译错误:
// 错误:使用内部 API
// import sun.misc.Unsafe;
// 正确:使用公开 API
import jdk.incubator.foreign.*;
// 错误:使用废弃 API
// String s = new String(bytes, "ISO-8859-1");
// 正确:使用新 API
String s = new String(bytes, StandardCharsets.ISO_8859_1);
修复编译错误确保代码能编译通过。
步骤四:运行测试
运行所有测试确保功能正常:
# 运行单元测试
mvn test
# 运行集成测试
mvn verify
# 运行所有测试
gradle test
运行测试确保功能正常。
步骤五:性能测试
进行性能测试确保性能不下降:
// 性能测试
// 1. 对比升级前后的性能
// 2. 检查 GC 性能
// 3. 检查内存使用
// 4. 检查 CPU 使用
性能测试确保性能不下降。
常见问题
问题一:内部 API 访问失败
内部 API 访问失败是最常见的问题:
// 问题:访问内部 API 失败
// import sun.misc.Unsafe; // 无法导入
// 解决方案:使用公开 API
import jdk.incubator.foreign.*;
// 使用外部函数和内存 API 替代 Unsafe
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemorySegment segment = MemorySegment.allocateNative(100, scope);
// 使用内存段
}
使用公开 API 替代内部 API。
问题二:第三方库不兼容
第三方库可能不兼容 JDK 17:
# 问题:第三方库不兼容
# 解决方案:
# 1. 更新库到最新版本
# 2. 寻找替代方案
# 3. 联系库维护者
更新库或寻找替代方案。
问题三:废弃 API 警告
废弃 API 可能产生警告:
// 问题:使用废弃 API
// @Deprecated
// public void oldMethod() {}
// 解决方案:使用新 API
public void newMethod() {
// 新实现
}
使用新 API 替代废弃 API。
实际迁移案例
案例一:Spring Boot 应用迁移
Spring Boot 应用迁移示例:
<!-- Spring Boot 配置 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.0.0</version> <!-- 支持 JDK 17 -->
</parent>
<properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
Spring Boot 3.0+ 支持 JDK 17,迁移相对简单。
案例二:Maven 项目迁移
Maven 项目迁移示例:
<!-- Maven 配置 -->
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
更新 Maven 配置支持 JDK 17。
案例三:Gradle 项目迁移
Gradle 项目迁移示例:
// Gradle 配置
plugins {
id 'java'
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
compileJava {
options.release = 17
}
更新 Gradle 配置支持 JDK 17。
迁移检查清单
迁移检查清单能帮你系统化地迁移:
// 迁移检查清单
// 1. 检查 JDK 版本
// - 确认当前版本
// - 确认目标版本
//
// 2. 检查代码兼容性
// - 使用 jdeps 检查内部 API
// - 使用 jdeprscan 检查废弃 API
// - 检查编译错误
//
// 3. 更新构建工具
// - 更新 Maven/Gradle 版本
// - 更新编译配置
//
// 4. 更新第三方库
// - 检查库兼容性
// - 更新到支持 JDK 17 的版本
//
// 5. 修复代码
// - 迁移内部 API
// - 更新废弃 API
// - 修复编译错误
//
// 6. 运行测试
// - 单元测试
// - 集成测试
// - 性能测试
//
// 7. 部署验证
// - 部署到测试环境
// - 充分测试
// - 部署到生产环境
迁移检查清单能帮你系统化地迁移。
注意事项
注意事项一:逐步迁移
建议逐步迁移,不要一次性升级:
// 逐步迁移
// 1. 先在测试环境测试
// 2. 逐步迁移模块
// 3. 充分测试
// 4. 最后迁移生产环境
逐步迁移能降低风险。
注意事项二:充分测试
迁移后要充分测试:
// 充分测试
// 1. 功能测试:确保功能正常
// 2. 性能测试:验证性能不下降
// 3. 兼容性测试:验证兼容性
// 4. 压力测试:验证稳定性
充分测试确保迁移成功。
注意事项三:回滚计划
准备回滚计划:
// 回滚计划
// 1. 保留旧版本代码
// 2. 准备回滚脚本
// 3. 测试回滚流程
// 4. 准备应急方案
准备回滚计划,以防万一。
最佳实践
最佳实践一:制定迁移计划
制定详细的迁移计划:
// 迁移计划
// 1. 评估迁移工作量
// 2. 制定时间表
// 3. 分配任务
// 4. 设置里程碑
制定详细的迁移计划,确保迁移顺利进行。
最佳实践二:使用工具检查
使用工具检查依赖和兼容性:
# 使用工具检查
# 1. jdeps:检查内部 API 依赖
# 2. jdeprscan:检查废弃 API
# 3. 编译测试:检查编译错误
使用工具检查,及时发现问题。
最佳实践三:文档记录
记录迁移过程和问题:
// 文档记录
// 1. 记录迁移步骤
// 2. 记录遇到的问题
// 3. 记录解决方案
// 4. 更新项目文档
记录迁移过程,方便后续维护。
总结
从 JDK 8/11 升级到 JDK 17 是个大工程,需要检查代码兼容性、更新第三方库、调整配置参数。这玩意儿虽然工作量不小,但 JDK 17 是 LTS 版本,性能和安全都有提升,值得升级。建议制定详细的迁移计划,逐步执行,充分测试,确保平滑升级。
建议在实际项目中尽早开始迁移,避免在 JDK 更新时出现问题。虽然迁移可能需要一些时间,但能让应用更稳定,性能更好。JDK 17 的新特性就介绍到这里了,兄弟们有啥问题随时问,鹏磊会尽量解答。