你如何dynamic编译和加载外部的java类?

(这个问题与我见过的很多问题类似,但大部分都不够具体,

背景:

我的程序的目的是使那些使用我的程序的人可以很容易地自定义“插件”,然后编译并加载到程序中以供使用(与在我的程序中实现不完整,缓慢的parsing器)。 我的程序允许用户将代码input到预定义的类中,扩展与我的程序打包在一起的编译类。 他们将代码input到文本窗格中,然后我的程序将代码复制到被覆盖的方法中。 然后将其保存为一个.java文件(几乎)为编译器做好准备。 该程序以保存的.java文件作为input运行javac(java编译器)。

我的问题是,如何获得它,使客户端可以(使用我编译的程序)保存这个Java文件(它扩展我的InterfaceExample)在他们的计算机上的任何地方,让我的程序编译它(不说“找不到符号:InterfaceExample” )然后加载它并调用doSomething()方法?

我不断地看到问答使用reflection或类加载器,几乎描述如何编译,但没有一个足够详细/我不完全理解他们。

看看JavaCompiler

以下是基于JavaDocs中给出的示例

这将在testcompile目录(基于package名称要求)中保存一个File ,并将File编译为一个Java类…

 import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import javax.tools.Diagnostic; import javax.tools.DiagnosticCollector; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; public class InlineCompiler { public static void main(String[] args) { StringBuilder sb = new StringBuilder(64); sb.append("package testcompile;\n"); sb.append("public class HelloWorld implements inlinecompiler.InlineCompiler.DoStuff {\n"); sb.append(" public void doStuff() {\n"); sb.append(" System.out.println(\"Hello world\");\n"); sb.append(" }\n"); sb.append("}\n"); File helloWorldJava = new File("testcompile/HelloWorld.java"); if (helloWorldJava.getParentFile().exists() || helloWorldJava.getParentFile().mkdirs()) { try { Writer writer = null; try { writer = new FileWriter(helloWorldJava); writer.write(sb.toString()); writer.flush(); } finally { try { writer.close(); } catch (Exception e) { } } /** Compilation Requirements *********************************************************************************************/ DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null); // This sets up the class path that the compiler will use. // I've added the .jar file that contains the DoStuff interface within in it... List<String> optionList = new ArrayList<String>(); optionList.add("-classpath"); optionList.add(System.getProperty("java.class.path") + ";dist/InlineCompiler.jar"); Iterable<? extends JavaFileObject> compilationUnit = fileManager.getJavaFileObjectsFromFiles(Arrays.asList(helloWorldJava)); JavaCompiler.CompilationTask task = compiler.getTask( null, fileManager, diagnostics, optionList, null, compilationUnit); /********************************************************************************************* Compilation Requirements **/ if (task.call()) { /** Load and execute *************************************************************************************************/ System.out.println("Yipe"); // Create a new custom class loader, pointing to the directory that contains the compiled // classes, this should point to the top of the package structure! URLClassLoader classLoader = new URLClassLoader(new URL[]{new File("./").toURI().toURL()}); // Load the class from the classloader by name.... Class<?> loadedClass = classLoader.loadClass("testcompile.HelloWorld"); // Create a new instance... Object obj = loadedClass.newInstance(); // Santity check if (obj instanceof DoStuff) { // Cast to the DoStuff interface DoStuff stuffToDo = (DoStuff)obj; // Run it baby stuffToDo.doStuff(); } /************************************************************************************************* Load and execute **/ } else { for (Diagnostic<? extends JavaFileObject> diagnostic : diagnostics.getDiagnostics()) { System.out.format("Error on line %d in %s%n", diagnostic.getLineNumber(), diagnostic.getSource().toUri()); } } fileManager.close(); } catch (IOException | ClassNotFoundException | InstantiationException | IllegalAccessException exp) { exp.printStackTrace(); } } } public static interface DoStuff { public void doStuff(); } } 

现在更新,包括为编译器提供一个类path,并加载和执行已编译的类!

我build议使用Java运行时编译器库。 你可以在内存中给它一个string,它会编译和加载类到当前的类加载器(或你select的一个),并返回加载的类。 嵌套的类也被加载。 注意:默认情况下,这完全在内存中运行。

例如

  // dynamically you can call String className = "mypackage.MyClass"; String javaCode = "package mypackage;\n" + "public class MyClass implements Runnable {\n" + " public void run() {\n" + " System.out.println("\"Hello World\");\n" + " }\n" + "}\n"; Class aClass = CompilerUtils.CACHED_COMPILER.loadFromJava(className, javaCode); Runnable runner = (Runnable) aClass.newInstance(); runner.run(); 

我不知道我从哪里了解到这一点,但是我会在两年前的一篇文章中回答我的解决scheme。 (我试图find我的post,并find了这个)。 这有点更丰富,我希望更清洁。 我当时的意图是将dynamic函数应用于数据(图像,文本或数据库),并立即在UI中查看结果。

我演示了如何获取一个string并将其保存到文件中,使用这些工具加载.java文件,使用自定义基类“Function”进行编译,创build一个Jar,加载jar和类,最后运行基类并重写dynamic类。

是否有可能使任何语言可执行?