JDK从8升级到21的问题集

京东云开发者
• 阅读 57

作者:京东工业 韦付芝

一、背景与挑战

1.升级动因

◦Oracle长期支持策略

◦现代特性需求:协程、模式匹配、ZGC等

◦安全性与性能的需求

◦AI新技术引入的版本要求

2.项目情况

◦100+项目并行升级的协同作战

◦多技术栈并存

◦持续集成体系的适配挑战



二、进度

应用总数 已完成 应用下线 待升级
100+ 73 13 10+



三、主要问题域与解决方案

1. 依赖管理的"蝴蝶效应"

•sun.misc.BASE64Encoder等内部API废弃 → 引发编译错误

•JAXB/JAX-WS从JDK核心剥离 → XML处理链断裂

•Lombok与新版编译器兼容性问题(尤其record类型)

核心原因在于JEP320提案:https://openjdk.org/jeps/320



案例1:历史SDK的编译陷阱

Compilation failure: Compilation failure:
#14 4.173 [ERROR] 不再支持源选项 6。请使用 8 或更高版本。
#14 4.173 [ERROR] 不再支持目标选项 6。请使用 8 或更高版本。
<!-- 旧版本编译器配置导致构建失败 -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.5</version>
    <configuration>
        <source>1.6</source>
        <target>1.6</target>
    </configuration>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.13.0</version>
    <configuration>
        <release>8</release><!-- 统一使用release参数 -->
    </configuration>
</plugin>

运行 HTML

案例2:JAXB的模块化剥离

javax.xml.bind.JAXBException:Implementation of JAXB-API has not been found
<dependency>
    <groupId>org.glassfish.jaxb</groupId>
    <artifactId>jaxb-runtime</artifactId>
    <version>4.0.5</version>
</dependency>

案例3:Lombok与新版编译器兼容性问题

java: java.lang.NoSuchFieldError
<dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <version>1.18.30</version>
</dependency>

案例4:Resource注解找不到

Caused by: java.lang.NoSuchMethodError: 'java.lang.String javax.annotation.Resource.lookup()'
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.<init>(CommonAnnotationBeanPostProcessor.java:664)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.lambda$buildResourceMetadata$0(CommonAnnotationBeanPostProcessor.java:395)
at org.springframework.util.ReflectionUtils.doWithLocalFields(ReflectionUtils.java:669)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.buildResourceMetadata(CommonAnnotationBeanPostProcessor.java:377)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.findResourceMetadata(CommonAnnotationBeanPostProcessor.java:358)
at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition(CommonAnnotationBeanPostProcessor.java:306)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyMergedBeanDefinitionPostProcessors(AbstractAutowireCapableBeanFactory.java:1116)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
... 37 more
<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>1.3.5</version>
</dependency>

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

上述两个依赖代码基本一样,推荐使用该版本:

jakarta.annotation:jakarta.annotation-api。



2. 模块化的破与立

反射访问的模块墙

[ERROR] Unable to make field private int java.text.SimpleDateFormat.serialVersionOnStream accessible
# 启动参数添加模块开放配置
--add-opens java.base/java.text=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED

完整模块开放配置模板

export JAVA_OPTS="-Djava.library.path=/usr/local/lib -server -Xmx4096m --add-opens java.base/sun.security.action=ALL-UNNAMED
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/sun.util.calendar=ALL-UNNAMED
--add-opens java.base/java.util.concurrent=ALL-UNNAMED
--add-opens java.base/java.util.concurrent.locks=ALL-UNNAMED
--add-opens java.base/java.security=ALL-UNNAMED
--add-opens java.base/jdk.internal.loader=ALL-UNNAMED
--add-opens java.management/com.sun.jmx.mbeanserver=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens java.base/sun.nio.ch=ALL-UNNAMED
--add-opens java.management/java.lang.management=ALL-UNNAMED
--add-opens jdk.management/com.sun.management.internal=ALL-UNNAMED
--add-opens java.management/sun.management=ALL-UNNAMED
--add-opens java.base/sun.security.action=ALL-UNNAMED
--add-opens java.base/sun.net.util=ALL-UNNAMED
--add-opens java.base/java.time=ALL-UNNAMED
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED"

3. 语法层面的"时空穿越"

案例1:Base64编解码改造

// JDK8写法(已废弃)
BASE64Encoder encoder =newBASE64Encoder();
String encoded = encoder.encode(data);
// JDK21规范写法
Base64.Encoder encoder =Base64.getEncoder();
String encoded = encoder.encodeToString(data);

案例2:日期序列化问题

Caused by:java.lang.reflect.InaccessibleObjectException: 
Unable to make field private int java.text.SimpleDateFormat.serialVersionOnStream accessible

解决方案

1.使用DateTimeFormatter替代SimpleDateFormat

2.或添加模块开放参数:--add-opens java.base/java.text=ALL-UNNAMED



4. 隐秘的"依赖战争"

注解包冲突典型案例

[ERROR] javax.annotation.Resource exists in both 
jsr250-api-1.0.jar and jakarta.annotation-api-1.3.5.jar
<!-- 统一使用Jakarta标准 -->
<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>
<!-- 排除旧版本依赖 -->
<exclusions>
    <exclusion>
        <groupId>javax.annotation</groupId>
        <artifactId>jsr250-api</artifactId>
    </exclusion>
</exclusions>

5. 构建体系的改造

Maven插件兼容性问题

[ERROR] The plugin org.apache.maven.plugins:maven-compiler-plugin:3.13.0 
requires Maven version 3.6.3

升级策略

1.升级Maven版本

2.统一插件版本

<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.13.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.4.0</version>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

四、最佳实践总结

1. 本地编译

第一步:在本地进行编译,提前识别出语法错误、版本冲突及不兼容问题。

主要有以下几种场景:

Base64:参照 【Base64编解码改造】

lombok:升级版本

jsr250、jaxb-runtime、jakarta.annotation-api:参照 【注解包冲突典型案例】

maven-compiler-plugin:升级版本

maven-resources-plugin:升级版本

maven-war-plugin:升级版本





2. 行云构建

同【本地编译】



3. 行云部署

a、镜像不匹配:自定义镜像或者使用已申请的jdk21镜像

b、module权限不够:参照【完整模块开放配置模板

c、JDSecurity加解密

所有数据库操作:important.properties配置文件的处理方式

classpath:important.properties 使用PropertyPlaceholderConfigurer进行处理,不要用JDSecurityPropertyFactoryBean。

<!--    <bean id ="secApplicationProperties" class="com.jd.security.configsec.spring.config.JDSecurityPropertyFactoryBean">-->
<!--       <property name="ignoreResourceNotFound" value="true" />-->
<!--       <property name="secLocation" value="classpath:important.properties"/>-->
<!--    </bean>-->



4. 运行

a、序列化异常

jdk21使用列表视图作为入参,导致jsf接口进行反序列化报错。报错代码如下:

List<String> subList = venderCodes.subList(i * batchSize, Math.min(venderCodes.size(), (i + 1) * batchSize));
VendorQueryVo vendorQueryVo = new VendorQueryVo();
vendorQueryVo.setVendorCodes(subList);
// 该接口最多支持100条调用
List<VendorVo> batchVendorNameByVendorCode = vendorBaseInfoService.getBatchVendorNameByVendorCode(vendorQueryVo, I18NParamFactory.getJDI18nParam());

将 vendorQueryVo.setVendorCodes(subList) 修改为vendorQueryVo.setVendorCodes(new ArrayList<>(subList)) 即可解决问题



b、线程上下文类找不到:使用多线程场景下尽可能使用显式指定线程池【默认情况下 不同运行环境的处理机制不同】





5. JVM调优

垃圾回收调优

UseParallelGCUseG1GCUseZGC是 Java 虚拟机(JVM)中三种不同的垃圾回收器(Garbage Collector, GC),它们的设计目标和使用场景有所不同。以下是它们的区别:

特性 UseParallelGC UseG1GC UseZGC
设计目标 高吞吐量 平衡吞吐量和延迟 极低延迟
暂停时间 较长 较短 极短
适用堆大小 中小堆(几 GB 到几十 GB) 大堆(几十 GB 到几百 GB) 超大堆(TB 级别)
CPU 消耗 中等 中等 较高
适用场景 批处理、计算密集型任务 对延迟有一定要求的应用 对延迟极其敏感的应用

•如果你的应用对吞吐量要求高,且可以接受较长的暂停时间,选择UseParallelGC

•如果你的应用对延迟有一定要求,且堆内存较大,选择UseG1GC

•如果你的应用对延迟极其敏感,且堆内存非常大,选择UseZGC

仅供参考,具体请按照实际情况来进行调整。

点赞
收藏
评论区
推荐文章
Stella981 Stella981
3年前
Druid时序数据库升级流程
目前Druid集群版本为0.11.0,新版本0.12.1已支持DruidSQL和Redis,考虑到Druid新特性以及性能的提升,因此需要将Druid从0.11.0版本升级到0.12.1版本,下面将对Druid升级步骤做详细的介绍,升级时请严格按照此步骤进行升级,以免出现一些不可预知的问题。1\.Druid升级包Dru
Stella981 Stella981
3年前
Dubbo版本升级
一、背景早期内部使用的是当当网fork的Dubbox,由于现在Dubbo又开始重新维护,而且阿里将其捐献给了Apache,并成为了Apache的顶级项目。因此dubbo版本升级到2.7.1势在必行。二、版本升级从2.8.4升级到2.7.1具体可以参考:DubboSpringBoot(v2.7.1): ht
Stella981 Stella981
3年前
KubeEdge在国家工业互联网大数据中心的架构设计与应用
摘要:在18年的时候,工信部开展了一个叫国家创新发展工程,这个工程中提出了要建立一个国家工业大数据中心,中国移动在其中承担了边缘协同与数据采集相关功能的研发。本文将从该项目背景下面临的问题与挑战、技术选型等方面进行阐述。一、问题与挑战需求从工厂采集生产、运行数据,汇总云端
Stella981 Stella981
3年前
Solaris11下Oracle12C集成部署问题处理及优化
Solaris11下Oracle12C集成部署问题处理及优化随着Oracle12C在企业级应用环境的不断扩大和升级部署,越来越的企业级传统应用项目逐步开始向Oracle12C进行迁移和流动。硬件及操作系统升级后也给Oracle12C的升级迁移带来了很多技术性挑战和考验。本文结合企业在新的Solaris11操作系统环境下集成安装Oracle12.2RA
轻量灵动: 革新轻量级服务开发 | 京东云技术团队
从JDK8升级到JDK17可以让你的应用程序受益于新的功能、性能改进和安全增强。下面是一些JDK8升级到JDK17的最佳实战:
JDK11升级JDK17最全实践干货来了 | 京东云技术团队
从JDK11到JDK17,到底带来了哪些特性呢?亚毫秒级的ZGC效果到底怎么样呢?值得我们升级吗?而且升级过程会遇到哪些问题呢?带着这些问题,本篇文章将带来完整的JDK11升级JDK17最全实践。
SpringBoot版本升级引起的FileNotFoundException——WebMvcConfigurerAdapter.class
缘起最近公司项目要求JDK从8升到17,SpringBoot版本从2.x升级到3.x,期间遇到了一个诡异的FileNotFoundException异常,日志如下(敏感信息使用xxx脱敏)logorg.springframework.beans.facto
京东云开发者 京东云开发者
3个月前
DeepSeek的开源之路:一文读懂从V1-R1的技术发展,见证从开源新秀到推理革命的领跑者
作者:京东科技蔡欣彤一、引言:AI时代的挑战与DeepSeek的崛起在大模型时代,AI技术的飞速发展带来了前所未有的机遇,但也伴随着巨大的挑战。随着模型规模的不断扩大,算力需求呈指数级增长,训练成本飙升,而性能提升的边际收益却逐渐递减,形成了所谓的“Sca
京东云开发者 京东云开发者
2个月前
DeepSeek的开源之路:一文读懂从V1-R1的技术发展,见证从开源新秀到推理革命的领跑者
作者:京东科技蔡欣彤一、引言:AI时代的挑战与DeepSeek的崛起在大模型时代,AI技术的飞速发展带来了前所未有的机遇,但也伴随着巨大的挑战。随着模型规模的不断扩大,算力需求呈指数级增长,训练成本飙升,而性能提升的边际收益却逐渐递减,形成了所谓的“Sca
阮小五 阮小五
1个月前
一键抠像+自动剪辑 Premiere Pro 2025 让创作更智能
核心功能介绍✅AI智能剪辑增强AutoReframe(自动重构图):智能识别主体,适配不同画幅(横屏→竖屏)。AI音频降噪:一键消除背景杂音,人声更清晰。智能色彩匹配:自动分析并统一多片段色调,告别手动调色。✅性能全面升级全新多帧渲染引擎:支持AppleS