編譯大型Java項目class沖突導致報錯的解決方案
前言
作為一名合格的JavaBoy,想必大家都遇到過調(diào)試大型項目的時候編譯完都遇到過一些奇奇怪怪的問題,比如說下面這種。

看起來是一個序列化框架報出來的異常,但是沿著堆棧排查又找不到什么線索,最后把剛開發(fā)的模塊排除后就會發(fā)現(xiàn)這個異常又消失了,代碼里并沒有用到報錯的類,這種情況很有可能遇到了二方庫中的類沖突。
本質(zhì)上就是項目模塊太多,依賴錯綜復雜,新模塊引入的二方庫中的類與原項目二方庫中的類沖突了??赡苁窃瓉硪蕾嚨陌姹九c新模塊中依賴的版本不一致,導致加載類結(jié)束后缺少一些類的情況。比較直觀的報錯有
java.lang.ClassNotFundException java.lang.NoSuchMethodError java.lang.NoClassDefFoundError java.lang.LinkageError
如果直接報出這些異常還有思路去排查,就怕報出一些奇奇怪怪的異常,今天盤點一下遇到類沖突之后如何解決。
查找沖突
mvn dependency:tree
maven提供了一個命令打印項目中所有的依賴樹,打印出的消息類似下面的圖,從樹中可以找到?jīng)_突class是來自哪個包哪個模塊。增加-Dverbose: verbose參數(shù)將詳細顯示項目所依賴的所有Jar包

但是這樣有個致命弱點,就是只能在開發(fā)階段排查,假如編譯期間報錯還好,如果運行期間加載到某個類出現(xiàn)沖突報錯就尷尬了,如果已經(jīng)打好包在集成環(huán)境出現(xiàn)這種問題,這種情況我們就無法通過命令直接去編譯源代碼查看沖突。
其次就是編譯項目太慢了,一個大型項目有幾十個模塊,編譯一次5分鐘過去了,雖然可以用著5分鐘去掘金摸會魚,但作為一個成熟的JavaBoy摸魚建立在把手頭工作搞好的前提下~
使用第三方工具排查
點名Arthas
bash腳本
這個腳本我屢試不爽,非常好用
#!/bin/bash
# 獲取傳入的參數(shù)
search_directory="$1"
search_class="$2"
# 檢查參數(shù)是否為空
if [ -z "$search_directory" ] || [ -z "$search_class" ]; then
echo "請?zhí)峁┮阉鞯哪夸浐皖愇募Q作為參數(shù)。"
exit 1
fi
# 搜索指定目錄及子目錄下的Jar包
find "$search_directory" -name "*.jar" -type f | while read -r jarfile; do
# 在Jar包中查找包含指定類文件名稱的文件
jar tf "$jarfile" | grep "$search_class" | while read -r classfile; do
# 輸出Jar包名稱和包含的類文件名稱
echo "Jar包:$jarfile 類文件:$classfile"
done
done
shell腳本也很簡單,執(zhí)行下面命令就能找到指定目錄下jar包中包含com.esotericsoftware.kryo.io.Input.inputStream類的包,這種方式不僅快而且很高效。
sh find_class.sh /tmp com.esotericsoftware.kryo.io.Input.inputStream
解決方案
找到?jīng)_突的類只是第一步,如何解決呢,這個問題本質(zhì)上是項目中出現(xiàn)了不同模塊引入了相同的類,如何定義相同得類,java的類加載器認為如果兩個class的全限定名完全一樣那就是相同的類。那想辦法讓這倆類的全限定名不一樣不就行了。
maven-shade-plugin
maven-shade-plugin是maven的一個插件,配置了這個插件打包的時候會自動觸發(fā)shade,shade直譯為陰影,顧名思義就是幫你把某個class藏起來。
hideOnBush
一般大型項目通常都是用maven來構(gòu)建項目,比如一個大項目有10個子模塊,你添加第11個子模塊時候com.google.common類沖突了,那么你在第11個模塊中配置如下
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>com.google.common</pattern>
<shadedPattern>shade.core.com.google.common</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
這樣在編譯的過程中com.google.common類會被隱藏為shade.core.com.google.common,全限定名不一樣了自然就不會再有沖突了。
解壓jar包
還有一種情況是通過引入外部jar包的方式引入依賴,代碼已經(jīng)被打成了jar包,但是jar包里有一些class沖突怎么辦呢。
這時候再用maven-shade-plugin也可以,但是一般我們都優(yōu)先修改外部的代碼,不輕易動原有的代碼結(jié)構(gòu),這時候有個騷操作就是解壓掉jar包,根據(jù)全限定名把沖突的包全刪了,再壓縮成jar包即可解決。
- 將jar包解壓到一個臨時文件夾中
unzip a.jar -d /tmp/tmpdir
- 進入文件夾刪除掉指定的包路徑
- 重新打包jar文件,注意最后有個.
jar cf a-new.jar -C /tmp/tmpdir .
將新的jar包重新加入模塊你會發(fā)現(xiàn)項目暢通無阻。
到此這篇關(guān)于編譯大型Java項目class沖突導致報錯的解決方案的文章就介紹到這了,更多相關(guān)Java class沖突內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot Starter中Bean 注冊與屬性綁定的兩大機制最佳實踐方案
在 Spring Boot的世界里,Starter(啟動器)是簡化依賴管理和自動化配置的強大工具,本文將深入探討 Spring Boot Starter 中 Bean 初始化的核心機制,并指出最佳實踐,感興趣的朋友跟隨小編一起看看吧2025-10-10
SpringBoot?SPI?機制和實現(xiàn)自定義?starter
這篇文章主要介紹了SpringBoot?SPI機制和實現(xiàn)自定義?starter,全稱是Service?Provider?Interface。簡單翻譯的話,就是服務提供者接口,是一種尋找服務實現(xiàn)的機制2022-08-08
基于java springboot + mybatis實現(xiàn)電影售票管理系統(tǒng)
這篇文章主要介紹了基于java springboot + mybatis實現(xiàn)的完整電影售票管理系統(tǒng)基于java springboot + mybatis,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08

