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 diagnosticListener,
     Iterable options,
     Iterable classes,
     Iterable compilationUnits)


//简单调用例子
boolean b = jc.getTask(null, fileManager, null, null, null, compilationUnits).call();




    我这里介绍的字符串形式的编译(其它方式也会有相似的具体实现),还需要提供一个FileObject一个实现类,将相应的对象封装作为getTask()的最后一个参数来构建具体的编译Task.

JavaDoc提供的一个FileObject参考实现:

Class JavaSourceFromString

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;
    }
}



完整的测试类:
Class TestDyCompile
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 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();
			}
		}
	}
}



我在这里的具体业务类为dyclass.Foo,也就是我们需要动态编译的类,为了方便写业务的调用代码,也可以让我们的业务类实现一个接口,然后通过反射获得具体子类强制转换来调用。

Test接口:

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();
		}


这段代码的作用相信大家一看到它就想到它的作用了,前面有说过JavaFileManager 的作用,我在这里设置了CLASS文件的输出目录,意图很简单,我的工程是在Eclipse运行的,项目的目标路径就是项目下的bin目录,如果不设置的话,class文件输出路径即为默认值,也就是直接在项目根路径下,后面直接调用就不能完成了。当然在其它一些应用场景中需要设置为自己需要的目录。
同样的方法可以设置JavaFileManager 其它的我们需要的文件规则属性(可以参照枚举类型StandardLocation),在这里就不一一介绍了。

     写东西的时候往往很难静下心来,写得很马虎,多多见谅,有机会大家多一起学习。

你可能感兴趣的:(Eclipse,应用服务器,项目管理,脚本,.net)