最近由于要写一个软件度量的工具,里面要用到动态编译JAVA文件,所以就学习了一下JAVA的动态编译机制。
在JAVA的工具包中有ToolProvider可以获得系统的编译器,然后根据相关接口对文件进行动态的编译,不多说了先上代码:
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;
public class DynamicCompiler {
public static void main(String[] args) throws Exception {
//获取java系统编译器
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
/**
* 获取文件管理器(管理需要编译的文件)
* 三个参数详见API文档
*/
StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
//可以根据需要填写多个参数,返回多个文件一块编译
Iterable it = fileMgr.getJavaFileObjects("D:/DynamicModel.java");
/**
* 获取编译任务
* 参数说明详见API文档
*/
CompilationTask task = compiler.getTask(null, fileMgr, null, null, null, it);
//编译java文件然后关闭文件管理器
task.call();
fileMgr.close();
/**
* 将class文件加载入内存,以供使用
* URL固定格式,如果是本地文件需以"file:/"开头
* URL参数用于指明class文件的根目录(不包含包名目录)
* loadClass的时候需要写上类的完整包名
*/
URL[] urls = new URL[]{new URL("file:/"+"D:/")};
URLClassLoader loader = new URLClassLoader(urls);
Class cl =loader.loadClass("DynamicModel");
DynamicModel dm =(DynamicModel)cl.newInstance();
dm.methodA();
dm.hello("World");
}
}
动态编译所需的JAVA文件如下(需要放置在D:/根目录下):
public class DynamicModel {
public void methodA() {
System.out.println("I am methodA");
}
public void hello(String str) {
System.out.println("Hello " + str);
}
}
说明:各个接口以及函数的调用可以参考API文档,里面有详细的说明,这里简单的说明几点。
(1) Iterable it = fileMgr.getJavaFileObjects("D:/DynamicModel.java");这里参数可以填写多个,虚拟机会把你所填入的所有文件返回给你,这样的话就可以一次性编译N个文件,可以减少开销。CompilationTask task = compiler.getTask(null, fileMgr, null, null, null, it);这里的详细调用参考API文档,第4个参数是Iterable类型的,指定编译文件所使用的参数,例如"-d d:/temp"指定生成的class文件保存在D/temp目录下,如果没有指定class文件保存的目录,默认保存在.java文件的同等目录下。
(2) 加载class文件的时候 需要使用URLClassLoader不能使用ClassLoader,因为ClassLoader只能加载在classpath下class文件,否则会抛出ClassNotFoundException;在创建URLClassLoader的时候需要指定class文件的根目录,这个目录不应该包含class文件的包名。例如下面的目录结构:
这是eclipse建立JAVA项目的常规目录结构,class文件放置在bin目录下,下面的com/XX/XX/XX...是包名所产生的目录,在指定URLClassLoader目录的时候只能指定"D:/cocomo/bin/"到此为止!注意最后的一个"/"一定要加上否则会报错,找不到class文件。然后loadClass()的时候就要填写要load的是哪个class,这时候要指定class的包名,即加上"com.xx.xx.DynamicMode",这里一定要注意要用写class的完整路径的时候一定要用".",而不是“/”,否则也找不到要load的Class文件。load出来之后就可以使用虚拟机提供给你的类了。
运行结果如下: