JSR 199引入了Java编译器API。如果使用JDK 6 的话,可以通过此API来动态编译Java代码。比如下面的代码用来动态编译最简单的Hello World类。该Java类的代码是保存在一个字符串中的。
import java.io.IOException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.SimpleJavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; import javax.tools.JavaCompiler.CompilationTask; /** * 在程序内部调用编译器,动态编译运行 * */ public class CompilerTest { public static void main(String[] args) throws Exception { String source = "public class Main { public static void main(String[]" +" args) {System.out.println(\"Hello World!\");} }"; JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null); StringSourceJavaObject sourceObject = new CompilerTest.StringSourceJavaObject("Main", source); String flag = "-d"; String outDir = System.getProperty("user.dir")+"/bin"; //设置Main的class目录也在eclipse默认编译的bin目录下,不设置则Main在abcc下 //Iterable<String> stringDir = Arrays.asList(flag,outDir); Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(sourceObject); CompilationTask task = compiler.getTask(null, fileManager, null,null, null, fileObjects); //CompilationTask task = compiler.getTask(null, fileManager, null,stringDir, null, fileObjects); boolean result = task.call(); if (result) { System.out.println("编译成功。"); //运行 Class<?> clazz = Class.forName("Main"); Method method = clazz.getMethod("main", new Class<?>[]{String[].class}); method.invoke(null, new Object[]{null}); } } static class StringSourceJavaObject extends SimpleJavaFileObject { private String content = null; public StringSourceJavaObject(String name, String content)throws URISyntaxException { super(URI.create("string:///" + name.replace('.', '/') + Kind.SOURCE.extension), Kind.SOURCE); this.content = content; } public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException { return content; } } }
运行此类但抛出ClassNotFoundException ,Main找不到。在eclipse 导航中发现Main类(代码中的动态编译类)class文件生成在工程主目录下,而在eclipse中建的类(CompilerTest)的编译class的位置在bin下,用ComplierTest加载器在其目录bin下加载显然就找不到了。
解决办法: Java编译器API实际上是调用系统环境中的javac命令,在终端下输入javac命令,会发现javac带有n多的参数,其中有一个是-d 可以指定编译后的class文件存放目录。但在java编译器的API是如何实现的呢? 可以在JavaCompiler的getTask方法进行设置:
CompilationTask getTask(Writer out, JavaFileManager fileManager, DiagnosticListener<? super JavaFileObject> diagnosticListener, Iterable<String> options,Iterable<String> classes,Iterable<? extends JavaFileObject> compilationUnits);其中的options就是指定了javac的参数。 在此将上面代码的注释去掉即可。