JRE 不使用可运行 jar 的清单文件指定的类路径

JRE not using classpath specified by manifest file of runnable jar

首先post,很抱歉我的格式不好。我有一个在 eclipse 中开发的 java 程序。我将程序导出为 jar (myJar.jar),然后我将程序依赖的所有外部 jar 放入位于同一位置的名为 lib 的文件夹中作为 myJar.jar。为了设置我的class路径,我有一个具有以下格式的清单文件:

Main-Class: exe.myMain
Class-Path: lib/jar_1.jar lib/jar_2.jar ... lib/jar_n.jar
Manifest-Version: 1.0

但是,当我尝试使用 "java -jar myJar.jar" 运行 程序时,lib 中的 classes 不是正在加载(我收到 ClassNotFoundException)。我在我的程序中使用了以下代码来打印 classpath:

ClassLoader cl = ClassLoader.getSystemClassLoader();
URL[] urls = ((URLClassLoader)cl).getURLs();
for(URL url:urls){
    System.out.println(url.getFile());
}

当我 运行 这段代码时 class 路径只是 "myJar.jar"。

我有两个问题:

1.) 上面的代码实际上是在 运行 时为我提供了 JRE 的 class 路径,还是只是向我提供了主 class 的地址?

2.) 鉴于上述代码确实在 运行 时为我提供了 JRE 的 class 路径,我做错了什么吗?

请随时索取更多信息,我会很乐意提供您所需要的。

您的 manifest 文件似乎有误。你有 Main-Class: exe.myMain => java 但有 exe 类型?推荐给你的是你应该使用 eclipse 来导出你的 jar 文件,它会自动为你创建所有的东西,你不必手动创建清单文件,所以你可能会犯错误。详见如下:

我已经通过使用 eclipse 创建了一个 java 项目进行了测试 => 使用 eclipse 的导出功能导出可运行的 JAR => 使用第三个选项(将所需的库复制到子文件夹中...) .导出后,我得到了名称为 myJarName.jar 的 jar 和名称为 myJarName_lib 的文件夹。使用 7zip 程序打开 jar => 在 META-INF => 打开 MANIFEST.MF => 下面是它的结构:

Manifest-Version: 1.0
Class-Path: . myJarName_lib/gson-2.8.0.jar myJarName_lib/jsoup-1.8.3.jar myJarName_lib/commons-cli-1.2.jar myJarName_lib/jackson-core-2.8.8.jar
Main-Class: upwork.Main

你所做的听起来是正确的。

出于某种原因,清单中的 Class-Path 条目在检查类路径时没有显示(例如 here and here;这些示例使用 属性 "java.class.path" 但我的测试表明 ClassLoader.getURLs() 表现相同)。不过,它仍应搜索 类。我不知道如何从清单中获取包含 Class-Path 条目的真实类路径。

我要检查的第一件事是 myJar.jar 中的文件 META-INF/MANIFEST.MF 是否与您创建的清单匹配。您可以通过将 myJar.jar 重命名为具有 .zip 文件扩展名来打开它。

我试图重现你的问题,但 lib/jar_1.jar 中的 类 是为我加载的,所以如果 META-INF/MANIFEST.MF 是正确的,我将详细描述我所做的,以便你可以发现我们所做的不同。

编辑:

以下是我使用的步骤:

  1. 创建一个名为 "experiment" 的新目录。下面的步骤都在该目录下完成。

  2. 创建名为 "jar_1"、"lib" 和 "exe"

  3. 的新目录
  4. 在目录 "jar_1" 中创建名为 "ClassInJar1.java" 的文件,内容如下:

    package jar_1;
    
    public class ClassInJar1 {
        public static void method() {
            System.out.println("Hello from ClassInJar1");
        }
    }
    
  5. 运行 javac jar_1/ClassInJar1.java

  6. 运行 jar cf lib/jar_1.jar jar_1/ClassInJar1.class

  7. 在目录 "exe" 中创建一个名为 "myMain.java" 的文件,内容如下:

    package exe;
    
    import java.net.*;
    import jar_1.ClassInJar1;
    
    public class myMain {
        public static void main(String[] args) {
            ClassLoader cl = ClassLoader.getSystemClassLoader();
            URL[] urls = ((URLClassLoader) cl).getURLs();
            for (URL url : urls) {
                System.out.println(url.getFile());
            }
            ClassInJar1.method();
        }
    }
    
  8. 运行 javac exe/myMain.java

  9. 在 "experiment" 目录中创建一个名为 "manifest" 的文件,内容如下:

    Main-Class: exe.myMain
    Class-Path: lib/jar_1.jar lib/jar_2.jar
    Manifest-Version: 1.0
  1. 运行 jar cfm myJar.jar manifest exe/myMain.class

  2. 运行 java -jar myJar.jar

输出:

/.../experiment/myJar.jar
Hello from ClassInJar1