关于NoClassDefFoundError问题


前言

有一次看到一篇文章,说到平时在工作时有一个习惯让他收获颇多,就是每次遇到问题或者bug按照一个固定格式做记录 –【日期】、【问题】、【原因】、【怎么发现的】、【修复】、【在哪些文件修改了】、【我导致的】、【解决Bug的时间】、【教训】。于是,我也记录了段时间,今天说说一个很基础也很常见的问题NoClassDefFoundError。


场景一

现象: 使用tddl时报错NoClassDefFoundError:

错误信息:
 Could not initialize class com.taobao.tddl.config.ConfigDataMode

配置信息:
<bean id="xxxDataSource" class="com.taobao.tddl.group.jdbc.TGroupDataSource" init-method="init">
        <property name="appName" value="xxx"/>
        <property name="dbGroupKey" value="xxx"/>
</bean>


分析:

(1) 初步的排查思路:(1)tddl jar包是否引用进来了,ConfigDataMode类是否确实存在?(2)是不是有包冲突?

之所以会强调一下排查(1),是因为以前犯过一个很低级的错误,在有多个子工程的情况下,eclipse全局搜该jar包发现确实有,但可能是你的主pom里做了该jar包的依赖管理,子pom里未进行依赖所导致的。而排查(2)应该算是在遇到这个错误时的第一反应。

(2) 打出依赖树,检查tddl-config是否有多版本?

没有,这里的版本为tddl-config-5.1.17-3.jar

(3) 进ConfigDataMode.class看看是否有什么特别?

这个类里面是有静态块的,而静态块代码是在类初始化时主动执行的,也就是静态块里的执行异常也可能导致上述的错误。

static {
        String m = System.getProperty(CONFIG_MODE, "auto");
        mode = Mode.nameOf(m);
        if (mode == null) {
            mode = Mode.AUTO;
        }

        String cmd = System.getProperty("sun.java.command");
        if (StringUtils.equals(cmd, "com.taobao.tddl.server.TddlLauncher")) {
            isServer = true;
        }

        String appname = System.getProperty("appName");
        if (StringUtils.startsWith(appname, "tddl")) {
            isServer = true;
        }
}

####(4) 强大的IDEA提示StringUtils.startsWith方法找不到(弱爆的eclipse什么反应都没有)。

于是再确认了下为什么,实际是commons-lang包的版本太低,没有提供,升级到2.6就可以了。


原因: 解决下来,其实是NoClassDefFoundError报错的类中包含的静态块代码初始化失败。



场景二

现象:工程在本地起服务没问题,分支部署到日常上就失败

查看/home/admin/wmpxbao/logs/catalina里的日志
发现又是一个NoClassDefFoundError:
Could not initialize class com.taobao.qa.perf.client.PapControllerClientFactory


分析:

(1) 有了上一次的经验后,很快检查jar包是否依赖成功?报错的类所在的jar包是否冲突?

没有。值得注意的是这次检查jar包冲突不再是本地工程就够了,更准确的方式是在服务器上war包的lib里检查。

(2) 接着按上面的经验看类中静态变量和静态块

在PapControllerClientFactory中发现整个类就是一堆静态变量、静态块、静态方法。也就是没有一些指导信息的话,排查会比较盲目。

(3) 于是只好从服务器上日志入手,清除干扰的历史日志,对所有新创建的日志进行仔细搜罗

最后在/home/admin/wmpxbao/logs下的wmpxbao.log主日志下捕捉到这么一点信息:

2016-07-26 ERROR proxy.ProxyService - [ProxyService]: 
newInstance Class.forName error, classType:com.taobao.wmpxbao.biz.schedule.PapTaskResultProcessor
java.lang.NoSuchMethodError: com.caucho.hessian.client.HessianProxyFactory.setChunkedPost(Z)V
at com.taobao.qa.perf.client.PapControllerFactory.createPAP(PapControllerClientFactory.java:126)

(4) 这么一来就有思路了,确定HessianProxyFactory在war包里的版本

发现有有两个

hessian-3.0.13.bugfix-20120711.024044-23.jar
hessian-3.1.5.jar

(5) 去maven仓库下载这两个jar source来确定哪一个包含我们需要的method,然后排除掉另一个。

排包麻烦时可以用:

<dependency>
  <groupId>hessian</groupId>
  <artifactId>hessian</artifactId>
  <version>999-not-exist</version>
</dependency>


原因:同样是NoClassDefFoundError报错,也同样是类静态块代码初始化时错误,但是从服务器上的日志去反向寻找异常点切入。


总结

NoClassDefFoundError的问题排查思路:

  • step1: 该类所使用的子module是否引入了jar包?
  • step2: 该类在该jar包中是否存在?
  • step3: 是否存在该类所在的jar包发生版本冲突?
  • step4: 该类中是否存在静态变量或静态代码块初始化失败?
crystal /
Published under (CC) BY-NC-SA in categories 后端  tagged with NoClassDefFoundError  Java