背景:在正常的环境下进行Excel导出是正常的,但是在单元测试的时候发现代码运行异常,抛出的异常堆栈如下:
Caused by: java.lang.VerifyError: class net.sf.cglib.core.DebuggingClassWriter overrides final method visit.(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at net.sf.cglib.core.AbstractClassGenerator.<init>(AbstractClassGenerator.java:38)
at net.sf.cglib.core.KeyFactory$Generator.<init>(KeyFactory.java:127)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:112)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:108)
at net.sf.cglib.beans.BeanMap$Generator.<clinit>(BeanMap.java:64)
at com.alibaba.excel.util.BeanMapUtils.create(BeanMapUtils.java:26)
at com.alibaba.excel.write.executor.ExcelWriteAddExecutor.addJavaObjectToExcel(ExcelWriteAddExecutor.java:144)
分析步骤:
步骤1:从网上搜该异常的解决方法中大部分都说cglib与asm版本不兼容导致的,那我遇到的这个问题是不是也是这种情况呢?从线上环境运行正常来看,排除了这种不兼容场景的想法,把线上的版本包下载下来,解压看到相关的jar如下:cglib-2.2.jar、asm-3.1.jar。
步骤2:是不是因为SpringBootTest加载了特定版本的asm呢?于是打开项目pom.xml,切换到Dependency Analyzer模式,输入asm,确实出现了asm的5.0.4版本,见下图:
选中上图的asm:5.0.4这一行,右击之后选择Exclude选项来排除该jar,也就是在pom.xml中增加
<exclusion>内部的代码:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
</exclusions>
</dependency>
满心欢喜的继续执行单元测试用例,得到的结果依然如下:
Caused by: java.lang.VerifyError: class net.sf.cglib.core.DebuggingClassWriter overrides final method visit.(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
at java.net.URLClassLoader.defineClass(URLClassLoader.java:468)
at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at net.sf.cglib.core.AbstractClassGenerator.<init>(AbstractClassGenerator.java:38)
at net.sf.cglib.core.KeyFactory$Generator.<init>(KeyFactory.java:127)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:112)
at net.sf.cglib.core.KeyFactory.create(KeyFactory.java:108)
at net.sf.cglib.beans.BeanMap$Generator.<clinit>(BeanMap.java:64)
at com.alibaba.excel.util.BeanMapUtils.create(BeanMapUtils.java:26)
at com.alibaba.excel.write.executor.ExcelWriteAddExecutor.addJavaObjectToExcel(ExcelWriteAddExecutor.java:144)
步骤3:既然已经排除了asm:5.0.4,为什么还会报这个错误呢?从报错的class net.sf.cglib.core.DebuggingClassWriter overrides final method visit.可以看到,DebuggingClassWriter重写的方法有问题,直接打开DebuggingClassWriter的源码,发现其继承自 ClassWriter,继续查看ClassWriter,这个时候惊掉了我的下巴,见下图:
加载的ClassWriter居然是accessors-smart-1.1.jar里面的,而不是我的直觉中asm-3.1.jar的ClassWriter,见下图:
不得不说,神奇的accessors-smart-1.1.jar,超出我的想象。既然1.1的不行,升级一下该jar吧,升级到1.2看看是否OK,pom.xml里面增加:
<dependency>
<groupId>net.minidev</groupId>
<artifactId>accessors-smart</artifactId>
<version>1.2</version>
<scope>test</scope>
</dependency>
打开1.2的内容,发现其移除了org.objectweb.asm相关的代码,见下图:
accessors-smart-1.2.jar依赖了asm-5.0.4.jar,将其排除,最终的pom.xml如下:
<dependency>
<groupId>net.minidev</groupId>
<artifactId>accessors-smart</artifactId>
<version>1.2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>asm</artifactId>
<groupId>org.ow2.asm</groupId>
</exclusion>
</exclusions>
</dependency>
再次运行单元测试用例,终于跑通过了。
总结:
1、对比差异,从差异中找到问题的关键,线上可以,单元测试不行,那肯定是单测的地方引入了问题
2、深入代码,看看加载的类具体是哪个jar里面的,找到问题的根因
3、升级相关的jar并排除相关依赖,最终问题解决。