一、背景與挑戰
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 或更高版本。
!-- 舊版本編譯器配置導致構建失敗 --?>
org.apache.maven.plugins/groupId?>
maven-compiler-plugin/artifactId?>
3.5/version?>
1.6/source?>
1.6/target?>
/configuration?>
/plugin?>
org.apache.maven.plugins/groupId?>
maven-compiler-plugin/artifactId?>
3.13.0/version?>
8/release?>!-- 統一使用release參數 --?>
/configuration?>
/plugin?>
運行 HTML
案例2:JAXB的模塊化剝離
javax.xml.bind.JAXBException:Implementation of JAXB-API has not been found
org.glassfish.jaxb/groupId?>
jaxb-runtime/artifactId?>
4.0.5/version?>
/dependency?>
案例3:Lombok與新版編譯器兼容性問題
java: java.lang.NoSuchFieldError
org.projectlombok/groupId?> lombok/artifactId?> 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.(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
jakarta.annotation/groupId?>
jakarta.annotation-api/artifactId?>
1.3.5/version?>
/dependency?>
javax.annotation/groupId?>
javax.annotation-api/artifactId?>
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標準 --?>
jakarta.annotation/groupId?>
jakarta.annotation-api/artifactId?>
2.1.1/version?>
/dependency?>
!-- 排除舊版本依賴 --?>
javax.annotation/groupId?>
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.統一插件版本
org.apache.maven.plugins/groupId?>
maven-compiler-plugin/artifactId?>
3.13.0/version?>
/plugin?>
org.apache.maven.plugins/groupId?>
maven-war-plugin/artifactId?>
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?>-->
?
4. 運行
a、序列化異常
jdk21使用列表視圖作為入參,導致jsf接口進行反序列化報錯。報錯代碼如下:
List subList = venderCodes.subList(i * batchSize, Math.min(venderCodes.size(), (i + 1) * batchSize)); VendorQueryVo vendorQueryVo = new VendorQueryVo(); vendorQueryVo.setVendorCodes(subList); // 該接口最多支持100條調用 List batchVendorNameByVendorCode = vendorBaseInfoService.getBatchVendorNameByVendorCode(vendorQueryVo, I18NParamFactory.getJDI18nParam());
將 vendorQueryVo.setVendorCodes(subList) 修改為vendorQueryVo.setVendorCodes(new ArrayList<>(subList)) 即可解決問題
?
b、線程上下文類找不到:使用多線程場景下盡可能使用顯式指定線程池【默認情況下 不同運行環境的處理機制不同】
?
?
5. JVM調優
垃圾回收調優
UseParallelGC、UseG1GC和UseZGC是 Java 虛擬機(JVM)中三種不同的垃圾回收器(Garbage Collector, GC),它們的設計目標和使用場景有所不同。以下是它們的區別:
| 特性 | UseParallelGC | UseG1GC | UseZGC |
| 設計目標 | 高吞吐量 | 平衡吞吐量和延遲 | 極低延遲 |
| 暫停時間 | 較長 | 較短 | 極短 |
| 適用堆大小 | 中小堆(幾 GB 到幾十 GB) | 大堆(幾十 GB 到幾百 GB) | 超大堆(TB 級別) |
| CPU 消耗 | 中等 | 中等 | 較高 |
| 適用場景 | 批處理、計算密集型任務 | 對延遲有一定要求的應用 | 對延遲極其敏感的應用 |
?如果你的應用對吞吐量要求高,且可以接受較長的暫停時間,選擇UseParallelGC。
?如果你的應用對延遲有一定要求,且堆內存較大,選擇UseG1GC。
?如果你的應用對延遲極其敏感,且堆內存非常大,選擇UseZGC。
僅供參考,具體請按照實際情況來進行調整。
審核編輯 黃宇
-
JDK
+關注
關注
0文章
83瀏覽量
17146
發布評論請先 登錄
為什么OCPP充電樁Security Profile要升級到2/3?
V5.0.0 ps 打印顯示線程 error 狀態全為 EINTRPT,,為什么?
明遠智睿RK3568核心板如何助力工業4.0升級
從“代步工具”到“智能伙伴”,存儲如何支撐汽車升級之路
從 400G 升級到 800G,到底能省多少錢?(洞察 TCO 真相)
從Modbus到Profinet:功率表通信升級的技術挑戰與解決方案
STM32MP157d linux從5.4.31升級到6.1.82無法啟動怎么解決?
STM32MP157d linux從5.4.31升級到6.1.82無法啟動怎么解決?
STM32MP157d linux從5.4.31升級到6.1.82無法啟動怎么解決?
XT大升級 加量不加價|Amass LC2.0升級版新品連接器破界而來
JDK從8升級到21的問題集
評論