JavaCompiler API:在 运行 时在已编译程序之外访问 functions/variables?

JavaCompiler API: access functions/variables outside the compiled program while it's running?

适配this以下代码采用class和函数名,一串Java代码,编译代码并运行函数。

public class Compile {

    static void compileAndRun(final String className, final String methodName, final String code) {

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();

        JavaFileObject file = new JavaSourceFromString(className, code);

        Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(file);
        JavaCompiler.CompilationTask task = compiler.getTask(null, null, diagnostics, null, null, compilationUnits);

        boolean success = task.call();

        for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {

            System.out.println(diagnostic.getCode());
            System.out.println(diagnostic.getKind());
            System.out.println(diagnostic.getPosition());
            System.out.println(diagnostic.getStartPosition());
            System.out.println(diagnostic.getEndPosition());
            System.out.println(diagnostic.getSource());
            System.out.println(diagnostic.getMessage(null));

        }
        System.out.println("Success: " + success);

        if (success) {

            try {

                URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { new File("").toURI().toURL() });
                Class.forName("HelloWorld", true, classLoader).getDeclaredMethod(methodName, new Class[] { String[].class }).invoke(null, new Object[] { null });

            } catch (ClassNotFoundException e) {
                System.err.println("Class not found: " + e);
            } catch (NoSuchMethodException e) {
                System.err.println("No such method: " + e);
            } catch (IllegalAccessException e) {
                System.err.println("Illegal access: " + e);
            } catch (InvocationTargetException e) {
                System.err.println("Invocation target: " + e);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
    }
}

class JavaSourceFromString extends SimpleJavaFileObject {

    final String code;

    JavaSourceFromString(String name, String code) {
        super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension),Kind.SOURCE);
        this.code = code;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return code;
    }
}

您可以这样称呼它:

    StringWriter writer = new StringWriter();
    PrintWriter out = new PrintWriter(writer);
    out.println("public class HelloWorld {");
    out.println("  public static void main(String args[]) {");
    out.println("    System.out.println(\"Hello World !\");");
    out.println("  }");
    out.println("}");
    out.close();

    Compile.compileAndRun("HelloWorld", "main", writer.toString());

是否可以从编译后的程序内部调用外部函数和变量?例如

class SomeClass {

    int x = 123;

    void myFunc() {

        StringWriter writer = new StringWriter();
        PrintWriter out = new PrintWriter(writer);
        out.println("public class HelloWorld {");
        out.println("  public static void main(String args[]) {");
        out.println("    y = foo(x);");
        out.println("  }");
        out.println("}");
        out.close();

        Compile.compileAndRun("HelloWorld", "main", writer.toString());
    }

    void foo(int x) {
    } 
}

或者可能在另一个 class 中使用 foox?显然我试过了,但是编译失败。有办法实现吗?

最终我能够使用 Groovy,它是 Java 语言的超集,来实现我的目标,所以你可以用 Java 或Groovy。传递变量和 类,当然可以包含变量和函数,是直截了当的,例如

class MyClass {

    int x = 100;

    void myFunc(int r) {

        System.out.println("value from inside script = " + r + " !");
    }
}

void groovy() {

    MyClass q = new MyClass();

    StringWriter writer = new StringWriter();
    PrintWriter out = new PrintWriter(writer);

    out.println("    System.out.println(\"Hello world\" + q.x);");
    out.println("    q.x += 100;");
    out.println("    q.myFunc(123)");

    out.close();

    // call groovy expressions from Java code
    Binding binding = new Binding();
    binding.setVariable("q", q);
    GroovyShell shell = new GroovyShell(binding);

    Object value = shell.evaluate(writer.toString());

    System.out.println("new value = " + q.x);
}