在一般情况下,开发人员都是在程序运行之前就编写完成了全部的Java源代码并且成功编译。对有些应用来说,Java源代码的内容在运行时刻才能确定。这个时候就需要动态编译源代码来生成Java字节代码,再由JVM来加载执行。典型的场景是很多算法竞赛的在线评测系统(如PKU JudgeOnline),允许用户上传Java代码,由系统在后台编译、运行并进行判定。在动态编译Java源文件时,使用的做法是直接在程序中调用Java编译器。
关于其他的动态编译的用法如动态加载单独文件里的类等,可参考其他API。
看参考http://www.iteye.com/topic/608485
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的参数。 在此将上面代码的注释去掉即可。
关于其他的动态编译的用法如动态加载单独文件里的类等,可参考其他API。
看参考http://www.iteye.com/topic/608485