使用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包沖突導致的問題已經解決。

 

更多文章

歡迎訪問更多關於消息中間件的原創文章:

關注微信公眾號

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM