Запуск класса Java (не jar) в качестве подпроцесса – это то, что мне нужно было сделать на этой неделе. Точнее, я хотел создать новый процесс из теста, вместо того, чтобы запускать его непосредственно внутри теста (в процессе). Я не думаю, что в этом есть что-то необычное или сложное. Но это не то, что мне когда-либо нужно было делать раньше, и я не знал точного кода для написания.
К счастью, быстрый поиск в Google и несколько сообщений StackOverflow позже. Я нашел ответ Мне было нужно. Хотя ответ есть, я переписываю его здесь для своей собственной выгоды, а также для вашей.
class JavaProcess {
private JavaProcess() {
}
public static int exec(Class clazz, List jvmArgs, List 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 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 и аргументами, которые ожидает метод класса main . Наличие доступа к обоим наборам аргументов позволяет полностью контролировать выполнение подпроцесса. Например, вы можете захотеть выполнить свой класс с низким объемом кучи, чтобы посмотреть, как он справляется с нехваткой памяти (для чего он мне и был нужен).
Обратите внимание, что для этого класс, который вы хотите выполнить, должен иметь метод main . 👈 Это в некотором роде важно.
Доступ к пути к исполняемому файлу Java (хранящемуся в javaBin ) позволяет выполнить подпроцесс, используя ту же версию Java, что и основное приложение. Если javaBin был заменен на "java" , то вы рискуете выполнить подпроцесс с версией Java по умолчанию на вашем компьютере. Вероятно, в большинстве случаев это нормально. Но, скорее всего, будут ситуации, когда это нежелательно.
Как только все команды будут добавлены в список команд , они передаются в ProcessBuilder . ProcessBuilder берет этот список и использует каждое содержащееся в нем значение для генерации команды. Каждое значение внутри списка команда разделяется пробелами ProcessBuilder . Существуют и другие перегрузки его конструктора, одна из которых принимает одну строку, в которой вы можете вручную определить всю команду самостоятельно. Это избавляет вас от необходимости вручную управлять добавлением аргументов в командную строку.
Подпроцесс запускается с передачей ввода-вывода процессу, который его выполнил. Это необходимо для просмотра любого stdout s и stderr то, что он производит. наследник является удобным методом и также может быть достигнут путем вызова цепочки вместо следующего кода (также настраивает stdin
builder
.redirectInput(ProcessBuilder.Redirect.INHERIT)
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
.redirectError(ProcessBuilder.Redirect.INHERIT);
Наконец-то wait For указывает исполняющему потоку дождаться завершения созданного подпроцесса. Не имеет значения, завершится ли процесс успешно или с ошибками. До тех пор, пока подпроцесс каким-то образом не завершится. Основная казнь может продолжаться. То, как процесс завершился, подробно описывается его значением выхода . Например, 0 обычно обозначает успешное выполнение и 1 детализирует недопустимую синтаксическую ошибку. Существует множество других кодов выхода, и все они могут варьироваться в зависимости от приложения.
Вызов метода exec будет выглядеть примерно так, как показано ниже:
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"
Я вырезал множество путей, включенных в путь к классам, чтобы сделать его немного более аккуратным. Ваш, вероятно, будет выглядеть намного длиннее этого. Это действительно зависит от вашего приложения на самом деле. Путь в приведенной выше команде – это минимум, необходимый для ее запуска (очевидно, настроенный для моей машины).
Метод exec является достаточно гибким и полезным при описании происходящего. Хотя, если вы хотите сделать его более гибким и применимым в более широком диапазоне ситуаций, я рекомендую вернуть сам ProcessBuilder из метода. Позволяет повторно использовать этот фрагмент кода в нескольких местах, обеспечивая при этом гибкость настройки перенаправления ввода-вывода, а также возможность решать, запускать ли подпроцесс в фоновом режиме или блокировать и ждать его завершения. Это будет выглядеть примерно так:
public static ProcessBuilder exec(Class clazz, ListjvmArgs, List 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 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); }
Используя одну (или обе) из этих функций, теперь у вас будет возможность запускать любой класс, существующий в пути к классам вашего приложения. В моей ситуации это было очень полезно при создании подпроцессов внутри интеграционного теста без необходимости предварительной сборки каких-либо банок. Это позволяло контролировать аргументы JVM, такие как память подпроцессов, которые нельзя было бы настроить, если бы они запускались непосредственно внутри существующего процесса.
Если вам понравился этот пост или вы сочли его полезным (или и то, и другое), пожалуйста, не стесняйтесь подписываться на меня в Твиттере по адресу @LankyDanDev и не забудьте поделиться с кем-либо еще, кто может счесть это полезным!
Оригинал: “https://dev.to/lankydandev/running-a-java-class-as-a-subprocess-34lm”