诊断运行时 java.lang.NoClassDefFoundError

一般看到 java.lang.NoClassDefFoundError 时, 下一步的操作很明确: 去看看这个类为什么不存在? 不在编译路径上? 这个类文件被损坏? 真的缺少这个类? 或者缺少包含这个类的包? 基本按照这个思路都能找到解决方法. 今天遇到这个情况是, 明明这个类完好无损的就在那里, 却始终抛出这个错误.

发现错误

一端代码运行时, 抛出了如下异常:

Exception in thread "pool-1-thread-82" java.lang.NoClassDefFoundError: org/jdom2/xpath/jaxen/JaxenCompiled
    at org.jdom2.xpath.jaxen.JaxenXPathFactory.compile(JaxenXPathFactory.java:82)
    at org.jdom2.xpath.XPathFactory.compile(XPathFactory.java:282)

初步诊断

查看这个出错的类: JaxenXPathFactory, 它依赖的类 JaxenCompiled 就和它同一个包, 点开这个类, 同样能打开
error.png

尝试对这个jar 包升级, 降级几个版本, 基本都报这个错. 为什么这个类明明存在, 却抛出这个异常呢?

代码诊断

我们尝试主动去加载这个缺少的类:

try {
    Class.forName("org.jdom2.xpath.jaxen.JaxenCompiled");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

得到如下异常:

Exception in thread "main" java.lang.NoClassDefFoundError: org/jaxen/NamespaceContext
    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 java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:264)
    at com.ebay.sre.adaas.service.impl.AuditServiceImpl.main(AuditServiceImpl.java:196)
Caused by: java.lang.ClassNotFoundException: org.jaxen.NamespaceContext
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    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)
    ... 15 more

它又告诉我们另外一个类不存在. 那么这个 org.jaxen.NamespaceContext, 而它属于 org.jdom/jaxen-core 这个 jar 包. 所以, 真相是: 并不是 JaxenCompiled 不存在, 而是它依赖的其他文件不存在, 导致我们收到 NoClassDefFoundError.

挖掘真相

为什么 jdom2 直接/间接依赖 org.jdom/jaxen-core, 而它缺不存在呢?
通过命令 mvn dependency:tree, 我们可以看到, 确实这个包并不依赖其它包

[INFO] com.tianxiaohui.sre:adaas:jar:0.0.2-SNAPSHOT
[INFO] +- org.jdom:jdom2:jar:2.0.6:compile
[INFO] +- org.springframework.boot:spring-boot-starter-jersey:jar:2.0.1.RELEASE:compile
[INFO] |  +- org.springframework.boot:spring-boot-starter:jar:2.0.1.RELEASE:compile
[INFO] |  |  +- org.springframework.boot:spring-boot:jar:2.0.1.RELEASE:compile

但是 maven 官方的网站上却显示这个jdom2 jar 包依赖另外 3 个 jar: link

<dependencies>
        <dependency>
            <groupId>jaxen</groupId>
            <artifactId>jaxen</artifactId>
            <version>1.1.6</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>xerces</groupId>
            <artifactId>xercesImpl</artifactId>
            <version>2.11.0</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>xalan</groupId>
            <artifactId>xalan</artifactId>
            <version>2.7.2</version>
            <optional>true</optional>
        </dependency>
    </dependencies>

但是呢, 这里的三个依赖都是 optional = true, 也就是可选的. 所以默认 mvn 并没有引入这些依赖.

修复

加入这 3 个依赖, 问题解决. 这里偷懒加入了 3 个依赖, 可能实际只是依赖其中 1 个或几个. Maven 官方有关 可选依赖的文档. link

标签: none

添加新评论