使用easyexcel時遇到Could not initialize class net.sf.cglib.beans.BeanMap$Generator
可以訪問這裡檢視更多關於大資料平臺建設的原創文章。
上一篇文章 Maven專案為什麼會產生NoClassDefFoundError的jar包衝突 結合了大量的圖解,詳細介紹了Maven專案產生jar包衝突的原因,以及為什麼在編譯的時候不報錯,在執行的時候會報錯的場景分析;
本篇記錄一下在專案開發中使用Alibaba的開源元件easyexcel做excel檔案上傳和下載功能時,遇到的一個jar包衝突問題的排查思路和解決辦法。
一. 問題現象
在使用alibaba的easyexcel工具開發excel的上傳和下載功能時,本機環境測試沒有問題,部署到測試環境時,卻發現下載excel檔案的功能一直異常,檢視後臺服務報錯日誌如下:
nested exception is com.alibaba.excel.exception.ExcelGenerateException: java.lang.NoClassDefFoundError: Could not initialize class net.sf.cglib.beans.BeanMap$Generator] with root cause] java.lang.NoClassDefFoundError: Could not initialize class net.sf.cglib.beans.BeanMap$Generator
上一篇Maven專案為什麼會產生NoClassDefFoundError的jar包衝突NoClassDefFoundError
類似的異常時,大多數都是因為jar包衝突引起的。
二. 問題排查流程
由於本地開發環境無法浮現,所以只能從測試環境著手排查。
1. 解壓jar包
登入專案部署的docker容器,解壓專案jar包,將解壓後的檔案放入 app 資料夾裡:
[root@08e08117bd99 /]# cd /data/application/java [root@08e08117bd99 /data/application/java]# unzip app.jar -d app/
2. 查詢jar包裡有沒有cglib.beans.BeanMap類
進入解壓後的 app 目錄,查詢 cglib 包:
[root@08e08117bd99 /data/application/java]# cd /app/BOOT-INF/lib $ ls -l | grep cglib -rw-r--r-- 1 root root 283080 Dec 7 2013 cglib-3.1.jar # 繼續解壓cglib包 [root@08e08117bd99 /data/application/java/app/BOOT-INF/lib]# unzip -l cglib-3.1.jar | grep BeanMap 336 12-07-2013 11:28 net/sf/cglib/beans/BeanMap$Generator$BeanMapKey.class 3219 12-07-2013 11:28 net/sf/cglib/beans/BeanMap$Generator.class 5008 12-07-2013 11:28 net/sf/cglib/beans/BeanMap.class 1825 12-07-2013 11:28 net/sf/cglib/beans/BeanMapEmitter$1.class 2090 12-07-2013 11:28 net/sf/cglib/beans/BeanMapEmitter$2.class 1546 12-07-2013 11:28 net/sf/cglib/beans/BeanMapEmitter$3.class 6339 12-07-2013 11:28 net/sf/cglib/beans/BeanMapEmitter.class
發現,解壓後的 jar包裡是存在cglib.beans.BeanMap$Generator
這個類。
3. 繼續看其它錯誤日誌
繼續看錯誤日誌,發現這段:
class net.sf.cglib.core.DebuggingClassWriter has interface org.objectweb.asm.ClassVisitor as super class
大概意思是:
ClassVisitor 定義的是一個 interface,但是現在卻作為一個父類(super class)被其它類繼承了。
這種情況表明,此時應該存在jar包衝突了:
-
ClassVisitor 在一個jar包中是一個 interface;
-
在另一個jar包中卻是一個 class。
4. 從剛剛解壓的專案jar包裡查詢asm包
由於這個報錯資訊裡包含asm,所以嘗試查詢包含asm的jar包:
發現竟然有兩個不同版本的jar包。。。
三. 問題原因
為什麼一個專案打出的 jar包裡會有兩個 asm 包呢?
1.專案哪些jar包依賴了asm包
使用 Maven helper 搜尋 asm:
-
easyexcel 2.1.6 依賴 cglib 3.1,cglib又依賴 asm 4.2;
-
專案的springboot版本是2.0.0.M6,底層會依賴 asm 3.1。
2. 為什麼會有兩個版本的asm包?
從Maven 官方網站裡搜尋 asm 包:
發現有兩個 artifactId 都叫 asm(但groupId 不一樣),點選第2個 asm,檢視詳情:
也就是說 gropuId 為 asm 的包,從3.3.1版本後不再維護了,後續版本遷移到 gropuId為 org.ow2.asm 的 asm 包。
看到這裡,結論已經出來:
-
asm 包從3.3.1 往後,gropuId 發生了變更(由asm 變更 org.ow2.asm);
-
由於專案使用的springboot版本是2.0.0,需要依賴asm3.0,easyexcel 2.1.6 依賴的是asm 4.2;
-
導致 Maven 在打包的時候將這兩個 asm包( artifactId 一樣,但groupId 不一樣)都打進去了。
四. 問題解決
1. 該使用哪個版本的asm包?
到現在為止,已經在排查過程中也得到了有用的報錯資訊:
class net.sf.cglib.core.DebuggingClassWriter has interface org.objectweb.asm.ClassVisitor as super class
翻譯過來就是: ClassVisitor 定義的是一個 interface,但是現在卻作為一個父類(super class)被其它類繼承了
並且也理清了產生衝突的原因:專案jar包裡包含兩個asm包,現在要確定專案使用哪個版本的asm包。
於是,從本機的 Maven 倉庫裡 分別找到 asm3.1 和 asm4.2 的包,並在 Idea 裡開啟ClassVisitor.class
:
asm3.1:
asm4.2:
可以看到:
-
asm3.1的
ClassVisitor.class
是 interface,asm4.2的ClassVisitor.class
是 class; -
再結合報錯資訊,確定專案應該使用 asm3.1 的包。
2. 怎麼解決衝突
由於asm 4.2是由 easyexcel 2.1.6 的依賴 cglib 3.1 引入的,因此降級 cglib 的版本,直接在pom.xml
裡引入低版本的 cglib 即可:
更新依賴,再次使用Maven helper檢視:
可以看到:asm 版本已經降級成功。
使用 Jenkins 編譯、打包、部署至測試環境後,再次測試專案的 excel 下載功能已經可以正常使用,jar包衝突導致的問題已經解決。
關注微信公眾號
歡迎大家關注我的微信公眾號閱讀更多原創文章: