Наличие аналога python exec в java

126
07 мая 2019, 05:00

Есть ли в Java функция, аналогичная функции exec в Python? Нигде не могу найти внятного ответа - есть или нет. Если есть, то хорошо было бы ее назвать.

Функция exec в python "выполняет" строку, как если бы она была написана в программе как кусок кода, а не строка. Например:

exec("a = func()")

Присвоит результат функции func в переменную a

Answer 1

В java 9 появился такой инструмент как java shell. Для вашей задачи он вполне подходит
Пример использование можно посмотреть в этой статье

Либо так в unix окружении:

String sourceCode = "1 + 2"
List<String> commands = new ArrayList<String>();
commands.add("/bin/sh");
commands.add("-c");
commands.add("echo \"" + sourceCode + "\"" | jshell");
SystemCommandExecutor commandExecutor = new SystemCommandExecutor(commands);
int result = commandExecutor.executeCommand();
StringBuilder stdout = commandExecutor.getStandardOutputFromCommand();
StringBuilder stderr = commandExecutor.getStandardErrorFromCommand();
System.out.println("STDOUT");
System.out.println(stdout);
System.out.println("STDERR");
System.out.println(stderr);
Answer 2

Прямого аналога указанной функции нет, однако имеется возможность постучаться к компилятору из java-кода и скормить ему исходник, после загрузив новый класс в загрузчик классов. Ну а после можно при помощи Reflection api запускать методы этого класса. На англоязычной версии имеются примеры: 1 , 2.

Я написал небольшую утилитку, которая компилирует и после запускает скрипт на вход которой нужно подать только сам скрипт и возвращаемый тип:

package ru.stackoverflow.question910353;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicLong;

public class ScriptRunner {
    private static final AtomicLong COUNTER = new AtomicLong(0);
    private static File root;
    private static String packageNamePath;
    private static String packageName;
    private static File packageNameFolder;
    private static URLClassLoader classLoader;
    static {
        try {
            root = Files.createTempDirectory("dynamicClasses").toFile();
            packageName = "ru.stackoverflow.question910353";
            packageNamePath = packageName.replaceAll("\\.", "/");
            packageNameFolder = new File(root, packageNamePath);
            packageNameFolder.mkdirs();
            classLoader = URLClassLoader.newInstance(new URL[]{root.toURI().toURL()});
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static <T> T run(String script, Class<T> returnType) throws Exception {
        String className = "Test" + COUNTER.incrementAndGet();
        String source = "package " + packageName + "; public class " + className + " implements java.util.concurrent.Callable<" + returnType.getName() + "> { " +
                " public " + returnType.getName() + " call()  throws Exception  { " + script + " } }";
        File sourceFile = new File(packageNameFolder, className + ".java");
        sourceFile.getParentFile().mkdirs();
        Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        compiler.run(null, null, null, sourceFile.getPath());
        Class<?> cls = Class.forName(packageName + "." + className, true, classLoader); 
        Callable<T> instance = (Callable<T>) cls.newInstance(); 
        return instance.call();
    }
}

И тест под неё, который доказывает работу этой утилиты:

package ru.stackoverflow.question910353;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class DynamicClassCompileTest {
    @Test
    public void testClassLoad() throws Exception {
        String result = ScriptRunner.run("return \"test\";", String.class);
        System.out.println("result "  + result);
        Assertions.assertEquals("test", result);
        Integer resultInt = ScriptRunner.run("" +
                "int a=5;" +
                "int b=10;" +
                "return a+b;", Integer.class);
        System.out.println("resultInt " + resultInt);
        Assertions.assertEquals(Integer.valueOf(15), resultInt);
        Assertions.assertThrows(Exception.class, () -> {
            ScriptRunner.run("invalid code",Void.class);
        });
    }

}

На деле же, чаще используется генерация байткода. Для этого существуют специальные библиотеки:

  1. cglib
  2. byte butty
READ ALSO
Сохранение даты в RethinkDB

Сохранение даты в RethinkDB

В RethinkDB нужно сохранить документ, в котором содержится несколько полей с датамиПо этим датам нужно будет искать документы в RethinkDB

141
Что означает direction &ldquo;1&rdquo; canScrollVertically(1)? [закрыт]

Что означает direction “1” canScrollVertically(1)? [закрыт]

На что влияет параметр 1 в recyclerViewcanScrollVertically(1)

138
Обьясните значение [закрыт]

Обьясните значение [закрыт]

Обьясните значение этого конструкотора, зачем он нужен и что он делает?

163
Повышение производительности REST API на спринге хайлоад

Повышение производительности REST API на спринге хайлоад

В нашем приложении мы хотим найти узкое место, из-за которого проседает производительностьНа REST API периодически выгружается огромная очередь...

198