最近由于要写一个软件度量的工具,里面要用到动态编译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); } }
(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出来之后就可以使用虚拟机提供给你的类了。
运行结果如下: