Dubbo服務(wù)調(diào)用JDK版本不兼容如何解決,你會(huì)嗎?
背景
最近,我們團(tuán)隊(duì)在開(kāi)發(fā)一個(gè)新服務(wù)時(shí),選擇了Spring Boot 3 + Dubbo 3 + JDK 17 的技術(shù)棧。
項(xiàng)目運(yùn)行比較順利,但在Dubbo RPC調(diào)用時(shí)遇到了一個(gè)棘手的問(wèn)題:其他團(tuán)隊(duì)的項(xiàng)目使用的是JDK 11,導(dǎo)致我們的JAR包在他們的環(huán)境中運(yùn)行時(shí)出錯(cuò)。
報(bào)錯(cuò)信息如下:
java: 無(wú)法訪問(wèn) com.xiaozou.rpc.XiaoZouService
錯(cuò)誤的類(lèi)文件: /Users/xiaozou/Desktop/sofe/java/localRepository/com/xiaozou/1.0.0-SNAPSHOT/xiaozou-1.0.0-SNAPSHOT.jar!/com/xiaozou/rpc/XiaoZouService.class
類(lèi)文件具有錯(cuò)誤的版本 61.0, 應(yīng)為 55.0
請(qǐng)刪除該文件或確保該文件位于正確的類(lèi)路徑子目錄中
從錯(cuò)誤提示中可以看出,JAR 包中的類(lèi)文件版本為 61.0(對(duì)應(yīng) JDK 17),而目標(biāo)環(huán)境期望的版本是 55.0(對(duì)應(yīng) JDK 11)。這引發(fā)了版本不兼容的問(wèn)題。
原因分析
Java 源代碼(.java 文件)通過(guò) javac 編譯成字節(jié)碼文件(.class 文件)時(shí),會(huì)在文件頭部寫(xiě)入版本信息。不同 JDK 版本生成的 .class 文件版本不同:
- Java 8:52.0
- Java 11:55.0
- Java 17:61.0
如果要驗(yàn)證我們可以直接使用idea自帶的反編譯工具直接查看/target/classes下的.class文件。
圖片
圖片
可以看到不同的JDK版本生成的.class文件頭部的版本信息是不一樣的,idea反編譯可以識(shí)別出來(lái)。
我們也可以使用javap命令查看.class文件的版本信息。
javap -v XiaoZou.class
那么我們就會(huì)得到如下字節(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
輸出中會(huì)顯示major version: 55,表示該類(lèi)文件是JDK 11 編譯的。
在jdk的源碼中verify_class_version方法就會(huì)對(duì)class的版本進(jìn)行校驗(yàn)
圖片
如果class的版本大于當(dāng)前的版本就會(huì)拋出UnsupportedClassVersionError異常
所以高版本JDK編譯的類(lèi)文件在低版本JDK上運(yùn)行時(shí),低版本JDK會(huì)直接編譯報(bào)錯(cuò)
解決方案
由于我們是dubbo RPC調(diào)用發(fā)布簡(jiǎn)單的RPC接口jar給到其他服務(wù)調(diào)用,除了基本的接口定義,不會(huì)使用高版本JDK的特性,所以解決方式相對(duì)來(lái)說(shuō)也比較容易。
如果是一些公用sdk就需要針對(duì)性發(fā)布不同jdk版本的sdk,這里不做討論
打包出一個(gè)低版本的jar
所以第一個(gè)解決方案是我們可以打一個(gè)低版本的jar給其他低版本的服務(wù)使用
我們項(xiàng)目的模塊是如下結(jié)構(gòu)
- xiaozou-service
- xiaozou-api
- xiaozou-start
我們?cè)趚iaozou-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,所以我們打出來(lái)的jar就是JDK 17的版本
但是我們可以在xiaozou-api模塊進(jìn)行覆蓋
在xiaozou-api的pom.xml添加如下配置
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
這樣xiaozou-api打出來(lái)的jar版本是JDK 11
低版本服務(wù)升級(jí)JDK
雖然這是一種解決方案,但升級(jí)JDK可能涉及較大的工程量和風(fēng)險(xiǎn),不太現(xiàn)實(shí)。
使用 Multi-Release JAR Files
JDK 9引入了Multi-Release JAR Files特性,允許在一個(gè)JAR文件中包含多個(gè)版本的類(lèi)文件。
雖然這可以解決版本兼容問(wèn)題,但配置較為復(fù)雜,不適合本場(chǎng)景。
總結(jié)
JDK 版本不兼容是Dubbo服務(wù)調(diào)用中常見(jiàn)的問(wèn)題,特別是在跨團(tuán)隊(duì)協(xié)作時(shí)。
解決這一問(wèn)題的最簡(jiǎn)單方法是打包一個(gè)低版本的JAR包,以確保兼容性。
如果不想折騰,保持團(tuán)隊(duì)之間JDK版本即可