如何在运行时在 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"});
我想从我当前的 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"});