服务治理之JDK8到JDK17

微服务
2025-05-14 21:09:28
分享

 

背景

随机技术的发展JDK开发工具不断迭代,目前公司所用的JDK8已经落后于时代技术趋势,一些新的技术特性无法在项目中使用。另外Springboot3的最低JDK版本要求JDK17。升级到 JDK 17(长期支持版本,LTS)具有多方面的优势,无论是从技术、性能、安全性还是未来生态支持的角度来看,都是值得考虑的。 为了安全、稳定、平滑的过度到SpringBoot3,故先对基础镜像的JDK升级到17。Spring官方推荐的JDK厂商是BellSoft。

起因

  • 业务发展需要接入到AI Chat接口,大家都知道AI的响应时间较长通常需要1-5秒才能响应完成。传统的阻塞式IO已经不能满足对三方慢接口调用的需求,阻塞IO用于慢接口对系统稳定性是比较大的挑战。我们需要使用WebFlux采用非阻塞式IO、Reactive响应式编程。故开始调研采用WebFlux技术后的兼容性。

  • OpenFeign声明式不能兼容WebFlux、三方的feign-reactive框架不兼容现SpingBoot版本,兼容问题只能选择WebClient的过度。

  • 正在考虑的SpringBoot3.x升级,现有的Springboot2.x社区已经停止维护,无法使用新的框架特性。

  • Spring Framework、SpringBoot、SpringCloud、SpringCloudAlibaba这些组件对版本一致性要求也高,兼容问题会踩较多的坑。最终决定首先升级JDK 再过度到Spirngboot3.x。

为什么不选择JDK21而是JDK17?

  • Spring Boot 2.x 的最终版本是 2.7.x(2022年11月停止维护),其设计时基于 JDK 8~17 测试,未针对 JDK 21 适配

  • Spring Boot 3.x(2022年11月发布)才是官方支持 JDK 17~21 的版本(需 Spring Framework 6+)

  • 某些功能(如Lombok、AOP)可能失效。

  • 第三方库(如数据库驱动、Redis客户端)可能不兼容 JDK 21。

JDK17的变化清单

  • JDK 17 是 Oracle 官方长期支持版本(LTS,支持到 2029年),而 JDK 8 的支持已逐渐减少(企业需付费扩展支持)。

  • 垃圾回收(GC)改进:ZGC(低延迟垃圾回收器)和 Shenandoah GC 在 JDK 17 中更成熟,适合高吞吐、低延迟场景。

  • JIT 编译器优化:GraalVM 等技术的引入提升了运行时性能。

  • 启动速度:JDK 9+ 的模块化系统(JPMS)减少了启动时间和内存占用。

  • Switch 表达式(JDK 14+):简化代码,支持模式匹配。

  • 文本块(Text Blocks)(JDK 15+):方便处理多行字符串。

  • Record 类(JDK 16):简化不可变数据类的定义。

  • 密封类(Sealed Classes)(JDK 17):限制类的继承关系,增强安全性。

  • 模式匹配(Pattern Matching):简化 instanceof 和类型转换操作。

  • 移除或废弃不安全/过时的 API(如 Security Manager 已标记为废弃)。

  • 更强的加密算法支持(如 TLS 1.3 默认启用)。

  • 漏洞修复:旧版本(如 JDK 8)的已知漏洞在新版本中被修复。

  • HTTP/2 客户端(标准 API,替代老旧的 HttpURLConnection)。

  • Vector API(JDK 16+):优化数值计算性能。

  • Foreign Function & Memory API(JDK 17):替代 JNI,更安全地调用本地代码。

  • JDK 10+ 对 Docker 等容器环境的资源限制(CPU/内存)感知更好。

  • Spring 6 / Spring Boot 3 已强制要求 JDK 17+。

  • 新框架和工具(如 Quarkus、Micronaut)优先支持新 JDK。

  • JDK 17 的 OpenJDK 版本可免费商用。

为什么现有技术架构可以升级到JDK17?

这点是在webflux-service做响应式验证的时候使用了JDK17 , 在整个Spring Framework5 + Springboot2.x + SpringCloud Hoxton.SR9 版本下没有出现大的问题,证明现有架构下升级到JDK17是可行的,但是也需要关注运行时的潜在问题。

另一方面是前期在流水线升级BellSoft-JDK17阶段性的调试成功,JDK17自定义镜像打包成功,标志着运行环境升级JDK17可以全面铺开。

JDK平滑升级

现有的CI/CD流水线中,其中maven构建环境升级到JDK17, 服务的运行基础镜像升级到JDK17,采用新的流水线脚本,可以修改.drone.yml文件切换打包过程使用的JDK版本。 

1747228063651.png

kind: template 
load: drone_deploy_jdk17_jar.yaml 
data:   
   name: ${DRONE_REPO_NAME}




kind: template 
load: drone_deploy_jar.yaml 
data:  
    name: ${DRONE_REPO_NAME}


Windows的BellSoft JDK17下载

\\192.168.12.97\软件工具\Java开发工具\JDK17

1747228655172.png


升级JDK17兼容问题处理记录

java.lang.reflect.InaccessibleObjectException: Unable to make field private final java.util.Map java.util.Collections$UnmodifiableMap.m accessible: module java.base does not "opens java.util" to unnamed module @48cf768c

错误的原因是因为 JVM 的模块 java.base 没有对未命名的模块开放 java.lang 这个包的深度反射 API 的调用权限。 具体来说,是没有开放 setAccessible(true) API。

这个问题在 JDK 8 以及以上的版本容易遇到。 解决的方法是在启动 Java 应用的时候, 加上参数指定开放特定的 Module/Package,使得 unnamed module 可以访问指定的 package 下面的深度反射 API。 如果有多个 Package 需要开放深度反射 API,那么可以指定多个 --add-opens 参数。

加上JVM参数配置:

--add-opens java.base/java.lang=ALL-UNNAMED 

上面的包名称根据报错中的opens后面的内容添加,因此根据上面报错,应该添加如下所示: 

--add-opens java.base/java.util=ALL-UNNAMED

1747228328366.png

相关文章:https://zhuanlan.zhihu.com/p/590864324

同类问题

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.xxx.tech.workorder.api.ApiWorkOrder': FactoryBean threw exception on object creation; nested exception is java.lang.reflect.InaccessibleObjectException: Unable to make field static final java.lang.invoke.MethodHandles$Lookup java.lang.invoke.MethodHandles$Lookup.IMPL_LOOKUP accessible: module java.base does not "opens java.lang.invoke" to unnamed module @1a87b51


Caused by: java.lang.NoSuchMethodError: 'java.lang.String javax.annotation.Resource.lookup()'

原因 :Maven: jakarta.annotation:jakarta.annotation-api:1.3.5  依赖与org.apache.tomcat:annotations-api:6.0.53 依赖的类冲突导致无法找到 lookup,解决办法去除对应的tomcat的annotations-api依赖,因为jakarta转正了





Caused by: java.lang.NullPointerException: Cannot invoke "java.lang.reflect.Method.invoke(Object, Object[])" because "com.sun.xml.bind.v2.runtime.reflect.opt.Injector.defineClass" is null


Caused by: java.lang.NoSuchFieldError: REFLECTION

   at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.(RuntimeModelBuilder.java:89)

   at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:456)

从 JDK 11 开始,JAXB 已被完全移除,需要手动添加依赖,这个代码预security有关系,jdk11以上会自动加jaxb依赖。

将jaxb-impl依赖版本调整为jaxb-api 2.3.1 版本一致,问题解决。



[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project activity-service-cloud: Compilation failure

177[ERROR] /drone/src/src/main/java/com/xxx/tech/activity/service/fkw/FkwService.java:[29,16] cannot find symbol

178[ERROR]   symbol:   class BASE64Encoder

179[ERROR]   location: package sun.misc  

问题原因:引用了不稳定的类,在新版本jdk中该类被移出了。

sun.misc.BASE64Encoder 是 JDK 内部 API,从 Java 8 开始就被标记为不推荐使用(deprecated),在更高版本的 JDK 中可能被移除。官方推荐使用 java.util.Base64 替代。 这种用法在 JDK 9+ 会失效(因模块化系统)不同 JDK 厂商(如 Oracle/OpenJDK/IBM等)可能不存在这个类


// Java 8+ 标准方式 
import java.util.Base64; 

Base64.Encoder encoder = Base64.getEncoder(); 
String encoded = encoder.encodeToString(data.getBytes()); 
Base64.Decoder decoder = Base64.getDecoder(); 
byte[] decoded = decoder.decode(encoded);



[ERROR] /drone/src/src/main/java/com/xxx/tech/payment/enums/CommonConstant.java:[3,43] package jdk.nashorn.internal.ir.annotations does not exist

[ERROR] /drone/src/src/main/java/com/xxx/tech/payment/enums/CommonConstant.java:[11,2] cannot find symbol

[ERROR]   symbol: class Reference

编译失败,原因:开发者不知情的情况下 引入了@Reference该注解,升级jdk17后导致编译报错。解决办法去除相关代码。

1747228608335.png



同类问题:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project dashboard-service-cloud: Compilation failure

[ERROR] /drone/src/src/main/java/com/xxx/platform/dashboard/server/utils/DateUtils.java:[5,48] package jdk.nashorn.internal.objects.annotations does not exist

1747228574489.png











The End
免责声明:本文系转载,版权归原作者所有;旨在传递信息,不代表本站观点和立场。