将Java类作为子进程运行

我本周需要将Java类(而不是jar)作为子进程运行。 更准确地说,我想从测试内部产生一个新进程,而不是直接在测试内部(进程内)运行它。 我不认为这是幻想或复杂的事情。 但是,这不是我以前不需要做的事,也不知道要编写的确切代码。

Luckily, a quick google and a few Stack Overflow posts later. I found the answer I needed. Although the answer is there, I am rewriting it here for my own benefit and as well as yours.

class JavaProcess {

  private JavaProcess() {
  }

  public static int exec(Class clazz, List<String> jvmArgs, List<String> args) throws IOException,
        InterruptedException {
    String javaHome = System.getProperty("java.home");
    String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
    String classpath = System.getProperty("java.class.path");
    String className = clazz.getName();

    List<String> command = new ArrayList<>();
    command.add(javaBin);
    command.addAll(jvmArgs);
    command.add("-cp");
    command.add(classpath);
    command.add(className);
    command.addAll(args);

    ProcessBuilder builder = new ProcessBuilder(command);
    Process process = builder.inheritIO().start();
    process.waitFor();
    return process.exitValue();
  }
}

此静态函数接受类您想要执行的代码以及所有JVM参数和该类的参数主要方法是期望的。 可以访问两组参数可以完全控制子流程的执行。 例如,您可能想以低堆空间执行您的类,以查看它在内存压力下如何应付(这是我需要的)。

请注意,要使其正常工作,您要执行的类需要具有一个主要方法。 👈这很重要。

访问Java可执行文件的路径(存储在javaBin),您可以使用与主应用程序相同的Java版本执行子流程。 如果javaBin被替换为“ java”,则存在使用计算机的默认Java版本执行子流程的风险。 很多时候那可能很好。 但是,在某些情况下可能不希望这样做。

将命令全部添加到命令列表,它们被传递给流程构建器。 的流程构建器 takes this list and uses each value contained in it to generate the 命令. Each value inside the 命令列表用空格分隔流程构建器. There are other overloads of its constructor, one of which takes in a single string where you can manually define the whole 命令 yourself. This removes the need for you to manually manage the addition of arguments to the 命令 string.

子进程以其IO传递到执行它的进程开始。 这要求同时看到两个标准输出和斯特德它产生的。继承IO是一种便捷的方法,也可以通过调用以下代码链来实现(也可以配置标准输入子流程):

builder
    .redirectInput(ProcessBuilder.Redirect.INHERIT)
    .redirectOutput(ProcessBuilder.Redirect.INHERIT)
    .redirectError(ProcessBuilder.Redirect.INHERIT);

最后等待告诉执行线程等待所生成的子进程完成。 该过程是否成功结束或错误都无关紧要。 只要子流程以某种方式完成。 主要执行可以继续进行。 过程如何完成由其详细描述exitValue。 例如,0通常表示成功执行,并且1个详细说明无效的语法错误。 还有许多其他退出代码,它们在应用程序之间可能会有所不同。

呼叫执行方法如下所示:

JavaProcess.exec(MyProcess.class, List.of("-Xmx200m"), List.of("argument"))

它执行以下命令(或其附近的命令):

/Library/Java/JavaVirtualMachines/jdk-12.0.1.jdk/Contents/Home/bin/java -cp /playing-around-for-blogs MyProcess "argument"

我剪掉了很多包含classpath的路径,以使其更加简洁。 您的外观可能会比这更长。 这实际上取决于您的应用程序。 上面命令中的路径是运行它所需的最低要求(显然是为我的机器定制的)。

的执行该方法相当灵活,有助于描述发生的情况。 虽然,如果您希望使其具有更大的延展性和适用性,但我建议您退还流程构建器方法本身。 允许您在多个地方重用这段代码,同时提供配置IO重定向的灵活性以及决定是在后台还是在块中运行子流程并等待其完成的能力。 这看起来像:

public static ProcessBuilder exec(Class clazz, List<String> jvmArgs, List<String> args) {
  String javaHome = System.getProperty("java.home");
  String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
  String classpath = System.getProperty("java.class.path");
  String className = clazz.getName();

  List<String> command = new ArrayList<>();
  command.add(javaBin);
  command.addAll(jvmArgs);
  command.add("-cp");
  command.add(classpath);
  command.add(className);
  command.addAll(args);

  return new ProcessBuilder(command);
}

通过使用这两个功能中的一个(或两个),您现在可以运行应用程序的类路径中存在的任何类。 在我的情况下,这对于在集成测试中生成子流程非常有用,而无需预先构建任何jar。 这样就可以控制JVM参数,例如子进程的内存,如果直接在现有进程内部运行,则这些子进程将无法配置。

If you enjoyed this post or found it helpful (or both) then please feel free to follow me on Twitter at @LankyDanDev and remember to share with anyone else who might find this useful!

from: https://dev.to//lankydandev/running-a-java-class-as-a-subprocess-34lm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值