背景
项目用 java 编写, ant 构建 , ivy 负责jar 包的依赖管理 ,在intelij 里边运行单元测试
java.lang.VerifyError: class com.fasterxml.jackson.databind.deser.SettableBeanProperty$Delegating overrides final method getDeclaringClass.()Ljava/lang/Class;
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:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
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:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
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:467)
at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
at com.fasterxml.jackson.module.afterburner.deser.PropertyMutatorCollector.addStringSetter(PropertyMutatorCollector.java:62)
at com.fasterxml.jackson.module.afterburner.deser.DeserializerModifier.findOptimizableProperties(DeserializerModifier.java:138)
at com.fasterxml.jackson.module.afterburner.deser.DeserializerModifier.updateBuilder(DeserializerModifier.java:58)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.buildBeanDeserializer(BeanDeserializerFactory.java:268)
at com.fasterxml.jackson.databind.deser.BeanDeserializerFactory.createBeanDeserializer(BeanDeserializerFactory.java:173)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer2(DeserializerCache.java:403)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createDeserializer(DeserializerCache.java:349)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCache2(DeserializerCache.java:264)
at com.fasterxml.jackson.databind.deser.DeserializerCache._createAndCacheValueDeserializer(DeserializerCache.java:244)
at com.fasterxml.jackson.databind.deser.DeserializerCache.findValueDeserializer(DeserializerCache.java:142)
at com.fasterxml.jackson.databind.DeserializationContext.findRootValueDeserializer(DeserializationContext.java:476)
at com.fasterxml.jackson.databind.ObjectReader._prefetchRootDeserializer(ObjectReader.java:1902)
at com.fasterxml.jackson.databind.ObjectReader.<init>(ObjectReader.java:185)
at com.fasterxml.jackson.databind.ObjectMapper._newReader(ObjectMapper.java:652)
at com.fasterxml.jackson.databind.ObjectMapper.readerFor(ObjectMapper.java:3383)
at tv.stickyads.mapstats.kafka.decoder.ViewJacksonDecoder.init(ViewJacksonDecoder.java:31)
at tv.stickyads.mapstats.kafka.decoder.ViewJacksonDecoder.<init>(ViewJacksonDecoder.java:25)
at tv.stickyads.mapstats.kafka.decoder.multi.ViewMultiDecoder.<clinit>(ViewMultiDecoder.java:13)
at tv.stickyads.mapstats.event.LazyView.<clinit>(LazyView.java:10)
at tv.stickyads.redstats.consumer.LLEConsumerTest.createLazyView(LLEConsumerTest.java:108)
at tv.stickyads.redstats.consumer.LLEConsumerTest.createConsumerRecord(LLEConsumerTest.java:87)
at tv.stickyads.redstats.consumer.LLEConsumerTest.runTest(LLEConsumerTest.java:54)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
因为项目中升级了 clickhousejdbc 从 0.2.4 升级到 0.2.6, 解决clickhouse jdbc connection response failed问题,这个问题的原因在另一篇文章提到过,
需要将 clickhouse jdbc 依赖的序列化库的 jackson 从2.7.5 升级到 2.9.10 ,否则一些 Unit Test 会报错 。
排查
升级后发现了上边的问题, 看错误描述 verify error 是class 加载时候格式校验出问题了,开始以为是JVM 版本的问题,所以为了排除这个问题, 从 command line 命令行运行 junit
junit 4 的命令行调用
java -cp $CLASSPATH org.junit.runner.JUnitCore 测试类 测试类
CLASSPATH 里边 要包含运行所需要的内容,包括 junit 相关的, 项目的测试类, product 类等内容以及所有的依赖, 当成一个 application 吧。
junit 3 略有不同
通过命令行执行,发现没有问题,那么说明JVM层面, JDK 层面肯定是没问题的。
这个时候想起来之前遇到的一个问题, 就是IDEA 里边用 Junit5 后,出现了类似的问题,也是class 文件的 verify 错误,
java - java.lang.VerifyError:类com.intellij.junit4.JUnit4TestRunnerUtil $ 5覆盖 final方法getRunner。()Lorg/junit/runner/Runner;
这个明显是 intellij 自身的工具类和 应用工程使用的版本不一致的冲突,因为入口类是 idea 提供的,所以会有 这种可能, idea 用junit4的工具类运行 junit5的处理过程,里边一些继承和重写不匹配的问题。
但是我们的问题表面看起来有一些不一样~
幸好 intellij 运行时候会把命令打印出来,查看java 执行命令,发现 classpath 中相关的jackson 依赖有多个版本,
/Users/xx/.ivy2/cache/com.fasterxml.jackson.core/jackson-core/bundles/jackson-core-2.8.9.jar:/Users/xx/.ivy2/cache/com.fasterxml.jackson.core/jackson-annotations/bundles/jackson-annotations-2.8.9.jar:/Users/xx/.ivy2/cache/com.fasterxml.jackson.core/jackson-databind/bundles/jackson-databind-2.8.9.jar
还有
/Users/xx/.ivy2/cache/com.fasterxml.jackson.core/jackson-core/bundles/jackson-core-2.9.10.jar:/Users/xx/.ivy2/cache/com.fasterxml.jackson.core/jackson-databind/bundles/jackson-databind-2.9.10.8.jar:/Users/xx/.ivy2/cache/com.fasterxml.jackson.core/jackson-annotations/bundles/jackson-annotations-2.9.10.jar
这下问题基本确定了,就是路径中有多个版本, 然后使用这个jar包的方式导致新增加的类关联到旧的类的方法,造成不一致,这个是依赖管理的很基本的问题了,本质上也是对工程的依赖没有十分清晰的掌握。
这里有两个方面,一个是ant 打包出来的 tarball 为什么没有问题,而且看 依赖的 lib 文件夹下,只有一个版本没有重复的,可以认为 ant打包的时候去重复了,对 jar 包的版本进行了选择,应该是选择了较高的版本。
另一个方面,在intellij 的依赖试图中可以看到,出现了多个版本的依赖,没想到最后执行命令的时候就把 界面上展示的依赖直接镜像过去了,没有什么去重复的操作。
结论
这个问题。本质上是作者把在IDE中的单测执行理解为和 jenkins 上CICD 的执行一样了,包括命令,包括最终的依赖版本。 可能大多数情况没有什么问题,但是对于多版本依赖,没有人工排除依赖,没有精确的依赖管理,在IDE中执行和 手动命令行或者 CI/CD 中是不一样的~
另外开始的时候并没有确定是因为依赖的原因导致单测出现问题, 引入clickhouse jdbc 在 maven仓库上对于 dependency 和 test dependency 都对其依赖的版本有了说明,是需要升级自己的project中的基础包的。还有一点令作者不明确的是,在CI/CD 中报错的不仅仅是 jackson,而是报错了很多project 中自定义的类,提示 class not found 等 ,还需要进一步深入分析~
本来只想升级一个clickhouse jdbc , 引入了这么多的问题, 还有新升级了jackson的版本,一些配置不一致的问题需要修的,真是 my god .