如何在运行时在 java 中设置多个类路径条目?

How to set multiple classpath entries in java at runtime?

我想从我当前的 java 项目中执行一个 java 程序。它有多个 jar 依赖项,应在执行它之前将其添加到类路径中。首先,我尝试使用普通 java 命令执行 -

  String classDir = "";
  for (int i = 0; i < compilerConfiguration.getClasspathEntries().size(); i++) {
        classDir = classDir + compilerConfiguration.getClasspathEntries().get(i) + ";";
  }
  runProcess("java -cp " + classDir + " topLevelProject.com.test.project.App");

  private static void runProcess(String command) throws Exception {
    Process pro = Runtime.getRuntime().exec(command);
    printLines(command + " stdout:", pro.getInputStream());
    printLines(command + " stderr:", pro.getErrorStream());
    pro.waitFor();
    System.out.println(command + " exitValue() " + pro.exitValue());
  } 

但是由于有多个类路径条目,它给我错误 -

  java.io.IOException: Cannot run program "java": CreateProcess error=206, The filename or extension is too long

classDir 的内容有点像这样 -

E:\test\maven\com.test.project\target\classes;C:\Users\dd\.m2\repository\p2\osgi\bundle\com.t.cep.studio.cli.3.0.164\com.t.cep.studio.cli-5.3.0.164.jar[+com/t/cep/studio/cli/studiotools/*;?**/*];C:\Users\dd\.m2\repository\p2\osgi\bundle\org.eclipse.core.runtime.11.1.v20150903-1804\org.eclipse.core.runtime-3.11.1.v20150903-1804.jar[~org/eclipse/core/internal/preferences/legacy/*;~org/eclipse/core/internal/runtime/*;+org/eclipse/core/runtime/*;?**/*];

或者,我尝试在执行 java 命令之前动态设置类路径:

  try {
        for (int i = 0; i < compilerConfiguration.getClasspathEntries().size(); i++) {
            String filePath = "file://" + compilerConfiguration.getClasspathEntries().get(i);
            URL[] url = { new URL(filePath) };
            ClassLoader currentThreadClassLoader = Thread.currentThread().getContextClassLoader();
            URLClassLoader urlClassLoader = new URLClassLoader(url, currentThreadClassLoader);
            Thread.currentThread().setContextClassLoader(urlClassLoader);
        }
        runProcess("java  topLevelProject.com.test.project.App");
    } catch (Exception e) {
        e.printStackTrace();
    }  

但它没有按预期设置类路径。还有其他解决方法吗?

我认为主要是命令行长度限制引起的OS问题,而不是java问题。我在玩 jdeps 时遇到了同样的问题,它还需要一个巨大的类路径。最后,我将类路径导出到一个纯文本文件中,并将该文件内容作为命令参数内联。

假设包含类路径字符串的文本文件的名称是:cp.txt

其内容(部分):

/home/anon/.m2/repository/com/app/generator/2.0.jar:/home/anon/.m2/repository/com/app/model/2.0.jar:/home/anon/.m2/repository/com/generator-helpers/2.0.jar:/home/anon/.m2/repository/org/eclipse/emf/org.eclipse.emf.ecore/2.10.1-v20140901-1043/org.eclipse.emf.ecore-2.10.1-v20140901-1043.jar:/home/anon/.m2/repository/org/eclipse/emf/org.eclipse.emf.common/2.10.1-v20140901-1043/org.eclipse.emf.common-2.10.1-v20140901-1043.jar:/home/anon/.m2/repository/org/eclipse/emf/org.eclipse.emf.ecore.xmi/2.10.1-v20140901-1043/org.eclipse.emf.ecore.xmi-2.10.1-v20140901-1043.jar:/home/anon/.m2/repository/commons-io/commons-io/2.1/commons-io-2.1.jar:etc...

那么你应该像这样执行你的命令:

runProcess("java -cp $(< cp.txt) topLevelProject.com.test.project.App");

它可以使用任何大小的类路径字符串,但它是一个 linux-only 解决方案。我不知道如何在 Windows 命令提示符中内联文件内容。好吧,至少我希望它能给你一些继续前进的想法。

除了@Szmeby 的回答,如果你不知道如何使用里面有类路径的文件,你可以尝试创建一个 "pathing jar"。

"Pathing jar" 仅包含 Manifest.mf 包含下一个条目的文件:

Class-Path: some.jar another.jar others.jar

您也可以使用通配符来减少长度。

从 Java 启动 Java 应用程序的另一种解决方案是使用 class 加载程序。这样做有以下优点:

  • 您可以保证新程序运行的 Java 版本与您的程序相同,因此不会出现多次安装问题。
  • 如果您愿意,新程序可以更轻松地与您的程序通信(尽管如果您选择这条路线,则完全不需要)。
  • 它更独立于平台,因为任何具有奇怪命令行配置的设置都不是问题。

要使用此解决方案,只需使用 URLClassLoader 加载所有必需的 jar,然后通过反射调用 main 方法:

URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{
    new URL("file://path/to/jar/1.jar"),
    new URL("file://path/to/jar/2.jar"),
    new URL("file://path/to/jar/3.jar"),
    new URL("file://path/to/jar/4.jar"),
    new URL("file://path/to/jar/5.jar")
});

Class<?> clazz = urlClassLoader.loadClass("topLevelProject.com.test.project.App");
clazz.getMethod("main", String[].class).invoke(null, new String[]{"Command", "Line", "Arguments", "Here"});