JDK6 动态编译——内存字符串编译方式
JDK6开始提供了动态编译的API,在许多应用场景都可以用得着,如动态加载(修改)服务、高性动态业务逻辑实现(用脚本或模板引擎实现效率满足不了需求)等都非常好用。
API对应的接口都在javax.tools包下面,常用编译方式有基于文本文件、内存字符串等,实际上基于URI的字节流都可以,也就是远程Java源代码也可以。对于常用的已有文件形式的动态编译网上的实例已经非常多,我在这里介绍下动态编译内存中以字符串的形式。
简单的代码流程如下:
//通过系统工具提供者获得动态编译器JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();//获得一个文件管理器,它的功能主要是提供所有文件操作的规则,//如源代码路径、编译的classpath,class文件目标目录等,其相关属性都提供默认值StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);//获得CompilationTask并调用//获得CompilationTask方法原型:getTask(Writer out, JavaFileManager fileManager, DiagnosticListener<? super JavaFileObject> diagnosticListener, Iterable<String> options, Iterable<String> classes, Iterable<? extends JavaFileObject> compilationUnits)//简单调用例子boolean b = jc.getTask(null, fileManager, null, null, null, compilationUnits).call();
import java.net.URI;import javax.tools.SimpleJavaFileObject;public class JavaSourceFromString extends SimpleJavaFileObject {/** * 源码 */ final String code; /** * 构造方法:从字符串中构造一个FileObject * @param name the name of the compilation unit represented by this file object * @param code the source code for the compilation unit represented by this file object */ JavaSourceFromString(String name, String code) { super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE); this.code = code; } @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) { return code; }}
import java.io.File;import java.io.IOException;import java.util.Arrays;import javax.tools.JavaCompiler;import javax.tools.JavaFileManager.Location;import javax.tools.JavaFileObject;import javax.tools.StandardJavaFileManager;import javax.tools.StandardLocation;import javax.tools.ToolProvider;import dyclass.Test;public class TestDyCompile {/** * * @author ZhangXiang * @param args * 2011-4-7 */public static void main(String[] args) {StringBuilder classStr = new StringBuilder("package dyclass;public class Foo implements Test{");classStr.append("public void test(){");classStr.append("System.out.println("Foo2");}}");JavaCompiler jc = ToolProvider.getSystemJavaCompiler();StandardJavaFileManager fileManager = jc.getStandardFileManager(null, null, null);Location location = StandardLocation.CLASS_OUTPUT;File[] outputs = new File[]{new File("bin/")};try {fileManager.setLocation(location, Arrays.asList(outputs));} catch (IOException e) {e.printStackTrace();}JavaFileObject jfo = new JavaSourceFromString("dyclass.Foo", classStr.toString());JavaFileObject[] jfos = new JavaFileObject[]{jfo};Iterable<? extends JavaFileObject> compilationUnits = Arrays.asList(jfos);boolean b = jc.getTask(null, fileManager, null, null, null, compilationUnits).call();if(b){//如果编译成功try {Test t = (Test) Class.forName("dyclass.Foo").newInstance();t.test();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}}}
public interface Test {//业务方法签名void test();}
Location location = StandardLocation.CLASS_OUTPUT; File[] outputs = new File[]{new File("bin/")};try {fileManager.setLocation(location, Arrays.asList(outputs));} catch (IOException e) {e.printStackTrace();}