編譯大型Java項目class沖突導(dǎo)致報錯的解決方案
前言
作為一名合格的JavaBoy,想必大家都遇到過調(diào)試大型項目的時候編譯完都遇到過一些奇奇怪怪的問題,比如說下面這種。
看起來是一個序列化框架報出來的異常,但是沿著堆棧排查又找不到什么線索,最后把剛開發(fā)的模塊排除后就會發(fā)現(xiàn)這個異常又消失了,代碼里并沒有用到報錯的類,這種情況很有可能遇到了二方庫中的類沖突。
本質(zhì)上就是項目模塊太多,依賴錯綜復(fù)雜,新模塊引入的二方庫中的類與原項目二方庫中的類沖突了??赡苁窃瓉硪蕾嚨陌姹九c新模塊中依賴的版本不一致,導(dǎo)致加載類結(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沖突導(dǎo)致報錯的解決方案的文章就介紹到這了,更多相關(guān)Java class沖突內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
spring中@Bean和@Component的區(qū)別及說明
文章主要介紹了@Bean和@Component兩個注解在Spring框架中的定義、作用范圍、創(chuàng)建方式、掃描和識別機制以及使用場景和建議2024-12-12解決MyBatis返回結(jié)果類型為Boolean的問題
這篇文章主要介紹了解決MyBatis返回結(jié)果類型為Boolean的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11SpringMvc自動裝箱及GET請求參數(shù)原理解析
這篇文章主要介紹了SpringMvc自動裝箱及GET請求參數(shù)原理解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習或者工作具有一定的參考學(xué)習價值,需要的朋友可以參考下2020-09-09Java基礎(chǔ)篇之serialVersionUID用法及注意事項詳解
這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)篇之serialVersionUID用法及注意事項的相關(guān)資料,SerialVersionUID屬性是用于序列化/反序列化可序列化類的對象的標識符,我們可以用它來記住可序列化類的版本,以驗證加載的類和序列化對象是否兼容,需要的朋友可以參考下2024-02-02SpringBoot webSocket實現(xiàn)發(fā)送廣播、點對點消息和Android接收
這篇文章主要介紹了SpringBoot webSocket實現(xiàn)發(fā)送廣播、點對點消息和Android接收,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-03-03