一般的java的动态编译是需要先生成java文件,然后编译成class,最后用classloader加载进来,生成最终的实例。
(1)通过ToolProvider类的静态方法getSystemJavaCompiler来得到一个JavaCompiler接口的实例。
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
(2)JavaCompiler中最核心的方法是run。通过这个方法可以编译java源程序。这个方法有3个固定参数和1个可变参数(可变参数是从Jave SE5开始提供的一个新的参数类型,用type… argu表示)。前3个参数分别用来为java编译器提供参数、得到Java编译器的输出信息以及接收编译器的错误信息,后面的可变参数可以传入一个或多个Java源程序文件。如果run编译成功,返回0。
int run(InputStream in, OutputStream out, OutputStream err, String... arguments)
如果前3个参数传入的是null,那么run方法将以标准的输入、输出代替,即System.in、System.out和System.err。如果我们要编译一个test.java文件,并将使用标准输入输出,run的使用方法如下:
int results = tool.run(null, null, null, "test.java");
很简单,但在Java 6中最好的方法是使用StandardJavaFileManager类。这个类可以很好地控制输入、输出,并且可以通过DiagnosticListener得到诊断信息,而DiagnosticCollector类就是listener的实现。
使用StandardJavaFileManager需要两步。首先建立一个DiagnosticCollector实例以及通过JavaCompiler的getStandardFileManager()方法得到一个StandardFileManager对象。最后通过CompilationTask中的call方法编译源程序。
/** * @MethodName : 编译java代码到Object * @Description : TODO * @param fullClassName 类名 * @param javaCode 类代码 * @return Object * @throws IllegalAccessException * @throws InstantiationException */ public Object javaCodeToObject(String fullClassName, String javaCode) throws IllegalAccessException, InstantiationException { long start = System.currentTimeMillis(); //记录开始编译时间 Object instance = null; //获取系统编译器 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); // 建立DiagnosticCollector对象 DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>(); // 建立用于保存被编译文件名的对象 // 每个文件被保存在一个从JavaFileObject继承的类中 ClassFileManager fileManager = new ClassFileManager(compiler.getStandardFileManager(diagnostics, null, null)); List<JavaFileObject> jfiles = new ArrayList<JavaFileObject>(); jfiles.add(new CharSequenceJavaFileObject(fullClassName, javaCode)); //使用编译选项可以改变默认编译行为。编译选项是一个元素为String类型的Iterable集合 List<String> options = new ArrayList<String>(); options.add("-encoding"); options.add("UTF-8"); options.add("-classpath"); options.add(this.classpath); JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, options, null, jfiles); // 编译源程序 boolean success = task.call(); if (success) { //如果编译成功,用类加载器加载该类 JavaClassObject jco = fileManager.getJavaClassObject(); DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(this.parentClassLoader); Class clazz = dynamicClassLoader.loadClass(fullClassName,jco); instance = clazz.newInstance(); } else { //如果想得到具体的编译错误,可以对Diagnostics进行扫描 String error = ""; for (Diagnostic diagnostic : diagnostics.getDiagnostics()) { error = error + compilePrint(diagnostic); } } long end = System.currentTimeMillis(); System.out.println("javaCodeToObject use:"+(end-start)+"ms"); return instance; }
运行DynaCompTest的main方法即可。