Dubbo服務調(diào)用JDK版本不兼容如何解決,你會嗎?
背景
最近,我們團隊在開發(fā)一個新服務時,選擇了Spring Boot 3 + Dubbo 3 + JDK 17 的技術棧。
項目運行比較順利,但在Dubbo RPC調(diào)用時遇到了一個棘手的問題:其他團隊的項目使用的是JDK 11,導致我們的JAR包在他們的環(huán)境中運行時出錯。
報錯信息如下:
java: 無法訪問 com.xiaozou.rpc.XiaoZouService
錯誤的類文件: /Users/xiaozou/Desktop/sofe/java/localRepository/com/xiaozou/1.0.0-SNAPSHOT/xiaozou-1.0.0-SNAPSHOT.jar!/com/xiaozou/rpc/XiaoZouService.class
類文件具有錯誤的版本 61.0, 應為 55.0
請刪除該文件或確保該文件位于正確的類路徑子目錄中
從錯誤提示中可以看出,JAR 包中的類文件版本為 61.0(對應 JDK 17),而目標環(huán)境期望的版本是 55.0(對應 JDK 11)。這引發(fā)了版本不兼容的問題。
原因分析
Java 源代碼(.java 文件)通過 javac 編譯成字節(jié)碼文件(.class 文件)時,會在文件頭部寫入版本信息。不同 JDK 版本生成的 .class 文件版本不同:
- Java 8:52.0
- Java 11:55.0
- Java 17:61.0
如果要驗證我們可以直接使用idea自帶的反編譯工具直接查看/target/classes下的.class文件。
圖片
圖片
可以看到不同的JDK版本生成的.class文件頭部的版本信息是不一樣的,idea反編譯可以識別出來。
我們也可以使用javap命令查看.class文件的版本信息。
javap -v XiaoZou.class
那么我們就會得到如下字節(jié)碼信息。
Last modified 2025年2月26日; size 229 bytes
SHA-256 checksum e8457714c4513babc6686fd96495e7ad7b65658b143023c8b8185bbacb757eea
Compiled from "XiaoZou.java"
public interface com.xiaozou.XiaoZou
minor version: 0
major version: 55
flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
this_class: #1 // com/xiaozou/XiaoZou
super_class: #3 // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 1
輸出中會顯示major version: 55,表示該類文件是JDK 11 編譯的。
在jdk的源碼中verify_class_version方法就會對class的版本進行校驗
圖片
如果class的版本大于當前的版本就會拋出UnsupportedClassVersionError異常
所以高版本JDK編譯的類文件在低版本JDK上運行時,低版本JDK會直接編譯報錯
解決方案
由于我們是dubbo RPC調(diào)用發(fā)布簡單的RPC接口jar給到其他服務調(diào)用,除了基本的接口定義,不會使用高版本JDK的特性,所以解決方式相對來說也比較容易。
如果是一些公用sdk就需要針對性發(fā)布不同jdk版本的sdk,這里不做討論
打包出一個低版本的jar
所以第一個解決方案是我們可以打一個低版本的jar給其他低版本的服務使用
我們項目的模塊是如下結構
- xiaozou-service
- xiaozou-api
- xiaozou-start
我們在xiaozou-service中配置了全局的打包插件
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
指定了source和target的版本為17,所以我們打出來的jar就是JDK 17的版本
但是我們可以在xiaozou-api模塊進行覆蓋
在xiaozou-api的pom.xml添加如下配置
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
這樣xiaozou-api打出來的jar版本是JDK 11
低版本服務升級JDK
雖然這是一種解決方案,但升級JDK可能涉及較大的工程量和風險,不太現(xiàn)實。
使用 Multi-Release JAR Files
JDK 9引入了Multi-Release JAR Files特性,允許在一個JAR文件中包含多個版本的類文件。
雖然這可以解決版本兼容問題,但配置較為復雜,不適合本場景。
總結
JDK 版本不兼容是Dubbo服務調(diào)用中常見的問題,特別是在跨團隊協(xié)作時。
解決這一問題的最簡單方法是打包一個低版本的JAR包,以確保兼容性。
如果不想折騰,保持團隊之間JDK版本即可