JDK從8升級到21的問題集(附案例代碼)
一、背景與挑戰(zhàn)
1.升級動因
?Oracle長期支持策略
?現(xiàn)代特性需求:協(xié)程、模式匹配、ZGC等
?安全性與性能的需求
?AI新技術(shù)引入的版本要求
2.項目情況
?100+項目并行升級的協(xié)同作戰(zhàn)
?多技術(shù)棧并存
?持續(xù)集成體系的適配挑戰(zhàn)
二、進(jìn)度
| 應(yīng)用總數(shù) | 已完成 | 應(yīng)用下線 | 待升級 |
|---|---|---|---|
| 100+ | 73 | 13 | 10+ |
三、主要問題域與解決方案
1. 依賴管理的"蝴蝶效應(yīng)"
•sun.misc.BASE64Encoder等內(nèi)部API廢棄 → 引發(fā)編譯錯誤
•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] 不再支持目標(biāo)選項 6。請使用 8 或更高版本。
<!-- 舊版本編譯器配置導(dǎo)致構(gòu)建失敗 -->
<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><!-- 統(tǒng)一使用release參數(shù) -->
</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
# 啟動參數(shù)添加模塊開放配置 --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規(guī)范寫法 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.或添加模塊開放參數(shù):--add-opens java.base/java.text=ALL-UNNAMED
4. 隱秘的"依賴戰(zhàn)爭"
注解包沖突典型案例
[ERROR] javax.annotation.Resource exists in both jsr250-api-1.0.jar and jakarta.annotation-api-1.3.5.jar
<!-- 統(tǒng)一使用Jakarta標(biāo)準(zhǔn) -->
<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. 構(gòu)建體系的改造
Maven插件兼容性問題
[ERROR] The plugin org.apache.maven.plugins:maven-compiler-plugin:3.13.0 requires Maven version 3.6.3
升級策略
1.升級Maven版本
2.統(tǒng)一插件版本
<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>
四、最佳實踐總結(jié)
1. 本地編譯
第一步:在本地進(jìn)行編譯,提前識別出語法錯誤、版本沖突及不兼容問題。
主要有以下幾種場景:
Base64:參照 【Base64編解碼改造】
lombok:升級版本
jsr250、jaxb-runtime、jakarta.annotation-api:參照 【注解包沖突典型案例】
maven-compiler-plugin:升級版本
maven-resources-plugin:升級版本
maven-war-plugin:升級版本
2. 行云構(gòu)建
同【本地編譯】
3. 行云部署
a、鏡像不匹配:自定義鏡像或者使用已申請的jdk21鏡像
b、module權(quán)限不夠:參照【完整模塊開放配置模板】
c、JDSecurity加解密
所有數(shù)據(jù)庫操作:important.properties配置文件的處理方式
classpath:important.properties 使用PropertyPlaceholderConfigurer進(jìn)行處理,不要用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使用列表視圖作為入?yún)?,?dǎo)致jsf接口進(jìn)行反序列化報錯。報錯代碼如下:
List<String> subList = venderCodes.subList(i * batchSize, Math.min(venderCodes.size(), (i + 1) * batchSize)); VendorQueryVo vendorQueryVo = new VendorQueryVo(); vendorQueryVo.setVendorCodes(subList); // 該接口最多支持100條調(diào)用 List<VendorVo> batchVendorNameByVendorCode = vendorBaseInfoService.getBatchVendorNameByVendorCode(vendorQueryVo, I18NParamFactory.getJDI18nParam());
將 vendorQueryVo.setVendorCodes(subList) 修改為vendorQueryVo.setVendorCodes(new ArrayList<>(subList)) 即可解決問題
b、線程上下文類找不到:使用多線程場景下盡可能使用顯式指定線程池【默認(rèn)情況下 不同運行環(huán)境的處理機(jī)制不同】
5. JVM調(diào)優(yōu)
垃圾回收調(diào)優(yōu)
UseParallelGC、UseG1GC和UseZGC是 Java 虛擬機(jī)(JVM)中三種不同的垃圾回收器(Garbage Collector, GC),它們的設(shè)計目標(biāo)和使用場景有所不同。以下是它們的區(qū)別:
| 特性 | UseParallelGC | UseG1GC | UseZGC |
|---|---|---|---|
| 設(shè)計目標(biāo) | 高吞吐量 | 平衡吞吐量和延遲 | 極低延遲 |
| 暫停時間 | 較長 | 較短 | 極短 |
| 適用堆大小 | 中小堆(幾 GB 到幾十 GB) | 大堆(幾十 GB 到幾百 GB) | 超大堆(TB 級別) |
| CPU 消耗 | 中等 | 中等 | 較高 |
| 適用場景 | 批處理、計算密集型任務(wù) | 對延遲有一定要求的應(yīng)用 | 對延遲極其敏感的應(yīng)用 |
•如果你的應(yīng)用對吞吐量要求高,且可以接受較長的暫停時間,選擇UseParallelGC。
•如果你的應(yīng)用對延遲有一定要求,且堆內(nèi)存較大,選擇UseG1GC。
•如果你的應(yīng)用對延遲極其敏感,且堆內(nèi)存非常大,選擇UseZGC。
僅供參考,具體請按照實際情況來進(jìn)行調(diào)整。
作者:京東工業(yè) 韋付芝
總結(jié)
到此這篇關(guān)于JDK從8升級到21問題集的文章就介紹到這了,更多相關(guān)JDK8升級到21的問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot+JavaMailSender實現(xiàn)騰訊企業(yè)郵箱配置
這篇文章主要介紹了SpringBoot+JavaMailSender實現(xiàn)騰訊企業(yè)郵箱配置,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Java 使用Axis調(diào)用WebService的示例代碼
這篇文章主要介紹了Java 使用Axis調(diào)用WebService的示例代碼,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-09-09
SpringBoot整合MyBatis四種常用的分頁方式(詳細(xì)總結(jié))
這篇文章詳細(xì)給大家總結(jié)了SpringBoot整合MyBatis四種常用的分頁方式,文中通過代碼示例為大家介紹的非常詳細(xì),需要的朋友可以參考下2023-07-07
java基本教程之synchronized關(guān)鍵字 java多線程教程
這篇文章主要介紹了java的synchronized原理、synchronized基本規(guī)則、synchronized方法 和 synchronized代碼塊、實例鎖和全局鎖2014-01-01
SpringBoot結(jié)果封裝和異常攔截的實現(xiàn)示例
SpringBoot 項目中,我們通常需要將結(jié)果數(shù)據(jù)封裝成特定的格式,以方便客戶端進(jìn)行處理,本文主要介紹了SpringBoot?優(yōu)雅的結(jié)果封裝和異常攔截,感興趣的可以了解一下2023-08-08
Activiti7與Spring以及Spring Boot整合開發(fā)
這篇文章主要介紹了Activiti7與Spring以及Spring Boot整合開發(fā),在Activiti中核心類的是ProcessEngine流程引擎,與Spring整合就是讓Spring來管理ProcessEngine,有感興趣的同學(xué)可以參考閱讀2023-03-03

