首页 > 解决方案 > 排除 JVM Jar 打开问题

问题描述

我有一个小型 Java 程序“Test.java”,它依赖于在同一目录中的单独 jar 文件中找到的类“TestLib”。

“项目”结构:

$ ls
Test.java   Test.class   testlib.jar

Test.java 和 testlib.jar 是 stub 工件,它们重现了我在更大的应用程序中看到的类路径问题。Test.java 所做的只是从 testlib.jar 实例化一个类,然后退出:

import com.TestLib;

public class Test {
    public static void main(String[] args) {
        System.out.println("Before instantiating testlib");
        new TestLib();
        System.out.println("After instantiating testlib");
    }
}

我的测试 jar 有问题的测试库类:

$ unzip -l testlib.jar
...
com/TestLib.class
...

(jar 包含许多其他类和 meta-inf 文件,此处省略)

我可以编译这个小项目:javac Test.java -classpath .:testlib.jar

我尝试使用 command 运行它java -classpath .:testlib.jar Test,并获得输出:

Before instantiating testlib
Exception in thread "main" java.lang.NoClassDefFoundError: com/TestLib at Test.main(Test.java:6)

它找不到“TestLib”!

有趣的是,当我将-verbose标志添加到我的 java 调用时,我可以看到它甚至从未打开我的库 jar 以在那里查找 TestLib:

$ java -verbose -classpath .:testlib.jar Test 2>&1 | grep "Opened"
[Opened /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/rt.jar]
[Opened /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jsse.jar]
[Opened /Library/Java/JavaVirtualMachines/jdk1.8.0_172.jdk/Contents/Home/jre/lib/jce.jar]

是什么决定了 JVM 是否打开一个 jar 文件来查找丢失的类?是否有任何 META-INF 或其他 jar 内容阻止 jar 被 JVM 打开?我应该采取哪些步骤来解决为什么在运行时找不到“TestLib”?

标签: javajvmclasspathclassloading

解决方案


就我而言,我看到这种行为是因为META-INF/INDEX.LIST我的 JAR 中存在一个文件。

该文件(如果存在)包含 JAR 中可用的类文件列表。我的 INDEX.LIST 文件不完整,并且没有提到我的TestLib班级。所以当 ClassLoaders 来找时,他们相信 jar 内容列表INDEX.LIST,并报告他们找不到类。

(由于对 Gradle 的“shadow”插件的一些非常规(读取,不正确)使用,我最终得到了一个不完整INDEX.LIST的结果。默认情况下,shadow 插件在构建阴影 jar 时会删除任何 INDEX.LIST 文件,但我不小心将其配置为不这样做。因此,如果您在有阴影的罐子中看到这种行为,这可能是原因。)


推荐阅读