在 Java 程序中运行 JAR
1、简介
在开发 Java 项目时,我们可能会遇到这样的情况:需要在 Java 程序中启动一个单独的进程运行外部 JAR(可执行 JAR)并查看输出,或者可能想要执行外部 JAR 中带有 main
方法的类文件。
2、运行可执行 JAR
可执行 JAR 是一种 JAR 文件类型,它包含一个设置了 Main-Class 属性的清单(manifest)文件。该属性指向应首先运行(使用 main
方法)的类文件。
我们可以使用 java -jar <example.jar>
命令从命令行运行该 JAR。也可以在 Java 程序中使用 ProcessBuilder
来实现类似的结果。
下面的代码演示了如何以编程式将可执行 JAR 作为单独进程运行,并在控制台中查看输出结果:
@Test
public void givenRunnableJar_whenExecuted_thenShouldRunSuccessfully() {
Process process = null;
try {
String jarFile = new File(Objects.requireNonNull(getClass().getClassLoader()
.getResource(RUNNABLE_JAR_PATH))
.toURI()).getAbsolutePath();
ProcessBuilder processBuilder = new ProcessBuilder("java", "-jar", jarFile);
processBuilder.redirectErrorStream(true);
process = processBuilder.start();
try (InputStream inputStream = process.getInputStream()) {
byte[] output = inputStream.readAllBytes();
System.out.println("Output: " + new String(output));
}
int exitCode = process.waitFor();
Assert.assertEquals("Process exited with an unexpected exit code", 0, exitCode);
} catch (IOException | InterruptedException | URISyntaxException e) {
Assert.fail("Test failed due to exception: " + e.getMessage());
} finally {
if (process != null) {
process.destroy();
}
}
}
首先,通过提供 JAR 文件(本例中为可执行 JAR)的绝对路径来创建文件对象。getClass().getClassLoader().getResource(RUNNABLE_JAR_PATH)
方法会获取资源 URL
,要确保 URL 不为 null
,并将其转换为 URI
。
然后,使用 java -jar
设置一个命令,这是执行可执行 JAR 的标准方法。接着,使用 ProcessBuilder
将 JAR 作为一个新进程执行。
开始运行后,我们会尝试从输入流中捕获程序输出。然后将输出转换成字符串并打印在控制台上。这与运行 java -jar
命令时看到的情况类似。
最后,使用 process.waitFor()
等待进程完成。如果退出代码(exit code)为零,则表示执行成功。如果退出代码为非零值,则表示在执行过程中发生了错误,测试将失败。
3、运行非可执行 JAR 文件
不可执行的 JAR 文件在其清单(manifest)文件中没有 Main-Class
属性。我们必须明确指定声明了 main
方法的类。
下面的代码演示了如何以编程式将不可执行 JAR 中的类文件作为单独进程执行,并在控制台中查看输出结果:
@Test
public void givenNonRunnableJar_whenExecutedWithMainClass_thenShouldRunSuccessfully() {
Process process = null;
try {
String jarFile = new File(Objects.requireNonNull(getClass().getClassLoader()
.getResource(NON_RUNNABLE_JAR_PATH))
.toURI()).getAbsolutePath();
String[] command = { "java", "-cp", jarFile, "com.company.HelloWorld", "arg1", "arg2" };
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.redirectErrorStream(true);
process = processBuilder.start(); // 开始进程
try (InputStream inputStream = process.getInputStream()) {
byte[] output = inputStream.readAllBytes();
System.out.println("Output: " + new String(output));
}
int exitCode = process.waitFor();
Assert.assertEquals("Process exited with an unexpected exit code", 0, exitCode);
} catch (IOException | InterruptedException | URISyntaxException e) {
Assert.fail("Test failed due to exception: " + e.getMessage());
} finally {
if (process != null) {
process.destroy(); // 资源清理
}
}
}
首先,通过提供 JAR 文件(本例中为不可执行 JAR)的绝对路径来创建 process.waitForobject
。
然后,使用 ProcessBuilder
设置执行环境。redirectErrorStream(true)
方法将错误流与标准输出流合并,这样我们就能在一个地方捕获所有输出。
然后通过 processBuilder.start()
将该命令作为一个新进程执行。我们从进程的输入流中捕获程序的输出,然后将其转换为字符串并打印到控制台上。输出结果与手动运行命令时的输出结果类似。
最后,通过 process.waitFor()
阻塞当前进程,直至任务完成。
如果退出代码为零,则表示 JAR 已成功执行;如果退出代码为非零值,则表示在执行 JAR 时出现了错误。
4、总结
本文介绍了如何在 Java 程序中使用 ProcessBuilder 来直接执行 JAR 文件(包括可执行 JAR 和非可执行 JAR),以及如何获取子进程的输出。
Ref:https://www.baeldung.com/java-execute-jar-file