背景

本周在与团队同学合并代码分支测试时遇到一个问题 AbstractMethodError,记录下排查思路和方法。业务上是一个 json 遍历的场景,使用的 json 库是 jackson,对于一个 org.codehaus.jackson.JsonNode 的实例对象(注意 jackson 中的 JsonNode 类是抽象类)调用其 asText 方法,然后就报了这个错误。

java.lang.AbstractMethodError: org.codehaus.jackson.JsonNode.asText()Ljava/lang/String;

从错误信息上看,调用到的方法是一个抽象方法,所以错误就是 AbstractMethodError ,出现这种问题的原因基本上就是 jar 包冲突了。由于对平时一直使用 fastjson,对 jackson 也不怎么熟悉,并且 JsonNode 类是一个抽象类,其子类比较多,不能靠猜和试,所以只能通过一些工具来确定原因。

使用 Arthas 排查

由于出现错误的环境是预发布环境(非开发环境),所以就想到了使用 Arthas 这个工具来排查。阿里巴巴开源的 Arthas 是一个非常好用的工具,以前使用过来排查过一些问题,算是还算熟悉了,不过也先浏览了一遍其支持的命令,才开始排查。 首先使用
Arthas 的 sc 命令看看加载到的 JsonNode 类和其所在的 jar 包,由于 sc 命令默认开启了子类匹配功能,所以所有当前类的子类也会被搜索出来,执行 sc 命令的部分结果如下(删掉不重要的信息)。

sc -d org.codehaus.jackson.JsonNode

class-info        org.codehaus.jackson.node.TextNode
code-source       /path/to/war/WEB-INF/lib/jackson-mapper-asl-1.8.8.jar
name              org.codehaus.jackson.node.TextNode
isInterface       false
isAnnotation      false
isEnum            false
isAnonymousClass  false
isArray           false
isLocalClass      false
isMemberClass     false
isPrimitive       false
isSynthetic       false
simple-name       TextNode
modifier          final,public
annotation
interfaces
super-class       +-org.codehaus.jackson.node.ValueNode
                 +-org.codehaus.jackson.node.BaseJsonNode
                   +-org.codehaus.jackson.JsonNode
                     +-java.lang.Object
class-loader      +-org.apache.catalina.loader.ParallelWebappClassLoader
                 +-java.net.URLClassLoader@5c4xd650
                   +-sun.misc.Launcher$AppClassLoader@58x4vac2
                     +-sun.misc.Launcher$ExtClassLoader@3bgcf4f

从上面的信息中可以看到加载到的 jackson-mapper-asl 的版本是 1.8.8。知道了这个信息,其实再去看看使用的 jackson 中 JsonNode 所在的包的版本,其实就可以知道具体原因了。但是为了深挖到底,继续使用  watch 命令查看出错前方法调用的参数是什么?具体命令如下

watch package.a.b.c methodd "{params,returnObj}" -x 2 

通过该命令可以看到出错前方法调用的完整 json 参数,结合代码里的属性,可知引起报错的应该是一个 org.codehaus.jackson.node.TextNode 类的对象,而该类又是 JsonNode 的子类。在 jackson-mapper-asl-1.8.8.jar 中,TextNode 类是不存在 asText 方法的,所以再调用该方法时就报了 java.lang.AbstractMethodError 这个错误。 再看看 JsonNode 所在的 jar 包的是 json-core-lgpl-1.9.13.jar,所以错误就非常明显了:JsonNode 类中有 asText 方法,而子类 TextNode 却没有对应的实现方法。

总结

本文介绍了一种运行时遇到的 jar 包冲突的排查思路,使用 Arthas 确实很方便。其实排查 jar 问题的方法有很多,比如对于 maven 项目使用 mvn dependency:tree 命令打印出依赖版本依赖树,又比如在 IDEA 中有 maven helper 这样的插件,可以查看各种包的冲突版本。上文介绍的使用 Arthas 也是一种方法,适合在没有 IDE 工具或者 maven 工具的场景使用。

Reference

  • https://alibaba.github.io/arthas/