1. 程式人生 > 實用技巧 >使用easyexcel時遇到Could not initialize class net.sf.cglib.beans.BeanMap$Generator

使用easyexcel時遇到Could not initialize class net.sf.cglib.beans.BeanMap$Generator

可以訪問這裡檢視更多關於大資料平臺建設的原創文章。

  1. 上一篇文章 Maven專案為什麼會產生NoClassDefFoundError的jar包衝突 結合了大量的圖解,詳細介紹了Maven專案產生jar包衝突的原因,以及為什麼在編譯的時候不報錯,在執行的時候會報錯的場景分析;

  2. 本篇記錄一下在專案開發中使用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 即可:

<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency>

更新依賴,再次使用Maven helper檢視:

​可以看到:asm 版本已經降級成功。

​使用 Jenkins 編譯、打包、部署至測試環境後,再次測試專案的 excel 下載功能已經可以正常使用,jar包衝突導致的問題已經解決。

關注微信公眾號

歡迎大家關注我的微信公眾號閱讀更多原創文章: