(此问题与我所见过的许多问题相似,但大多数问题对我的工作不够具体)
背景:
我的程序的目的是使使用我的程序的人可以轻松制作自定义的“插件”,然后将其编译并加载到程序中使用(与在我的程序中实现的不完整,缓慢的解析器相比)。我的程序允许用户将代码输入到预定义的类中,从而扩展了与我的程序一起打包的已编译类。他们将代码输入到文本窗格中,然后我的程序将代码复制到被覆盖的方法中。然后,将其保存为(几乎)准备供编译器使用的.java文件。该程序以保存的.java文件作为输入来运行javac(java编译器)。
我的问题是,如何获取它,以便客户端(使用我的编译程序)可以将此java文件(扩展了InterfaceExample)保存在他们的计算机上的任何位置,让我的程序对其进行编译(不说“找不到符号:InterfaceExample” ),然后加载它并调用doSomething()方法?
我一直看到Q&A使用反射或ClassLoader,并且几乎描述了如何编译它,但是对于我来说,没有一个足够详细的描述,我不完全理解它们。
以下内容基于JavaDocs中给出的示例
这会将a保存File在testcompile目录中(基于package名称要求),然后将其编译File为Java类。
a
File
testcompile
package
package inlinecompiler; 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") + File.pathSeparator + "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(); } }
现在已更新,包括为编译器提供类路径以及已编译类的加载和执行!