有时候我们需要把一些外部资源(比如第三方jar,自己动态生成的java文件)编译并加载到classloader,这时我们就需要JavaCompiler 个类,jdk是从1.6开始支持此功能
网上有很多类似列子,搜索“动态编译”可以找到,我这里只列出一种实现方法(按原理分好像有三种可参考:http://www.cnblogs.com/flyoung2008/archive/2011/11/14/2249017.html)
public class TestClient {
public static void main(String[] args) throws Exception {
String c ="package webservice;\n"+
"public class Hello{\n" +
"\n" +
" public static void main(String[] args) {\n" +
" System.out.println(\"初始化成功了!\");\n" +
" }\n" +
"}\n";
/**
* 先把文件写入磁盘
*/
String src = "/src";
String bin = "/bin";
File srcdir = new File(System.getProperty("user.dir") + src+"/webservice"); //java目录
File classdir = new File(System.getProperty("user.dir") + bin+"/webservice"); //.class目录
// 如果 \temp 不存在 就创建
if (!srcdir.exists()) {
srcdir.mkdir();
}
FileWriter writer = new FileWriter(new File(srcdir,"Hello.java"));
writer.write(c);
writer.flush();
writer.close();
/**
* 从磁盘中加载类并编译
*/
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager javaFileManager = javaCompiler.getStandardFileManager(null, null, null);
Iterable it = javaFileManager.getJavaFileObjects(new File(srcdir,"Hello.java"));
JavaCompiler.CompilationTask task = javaCompiler.getTask(null, javaFileManager, null, null, null, it);
Boolean call = task.call();
javaFileManager.close();
System.out.println(call);
/**
* 生成URLClassLoader 加载.class文件
*路径为.class文件的上级目录
* 生成的.classs文件包名要与loadClass(className)中className一致
* url前面要加上file://,不能直接写文件路径
*/
URL url[] = new URL[]{new URL("file://D:/user/828470/workspace2/TestForJava/src")};
URLClassLoader loader = new URLClassLoader(url);
loader.loadClass("webservice.Hello").getDeclaredMethod("main",String[].class).invoke(null,new String[]{null});
Class.forName("webservice.Hello").getDeclaredMethod("main",String[].class).invoke(null,new String[]{null});
}
}
getTask()方法 jdk描述
JavaCompiler.CompilationTask getTask(Writer out,
JavaFileManager fileManager,
DiagnosticListener<? super JavaFileObject> diagnosticListener,
Iterable<String> options,
Iterable<String> classes,
Iterable<? extends JavaFileObject> compilationUnits)
使用给定组件和参数创建编译任务的 future。该编译可能没有完成,正如 CompilationTask 接口中所述。
如果提供了文件管理器,则它必须能够处理 StandardLocation 中定义的所有位置。
参数:
out - 用于来自编译器的其他输出的 Writer;如果为 null,则使用 System.err
fileManager - 文件管理器;如果为 null,则使用编译器的标准文件管理器
diagnosticListener - 诊断侦听器;如果为 null,则使用编译器的默认方法报告诊断信息
options - 编译器选项;null 表示没有选项
classes - 类名称(用于注释处理),null 表示没有类名称
compilationUnits - 要编译的编译单元;null 表示没有编译单元
返回:
表示编译的对象
抛出:
RuntimeException - 如果在用户提供的组件中发生不可恢复的错误。cause 为用户代码中的错误。
IllegalArgumentException - 如果给定的任一编译单元具有不同于 source 的类型
注:代码中的注释部分是特别需要注意的地方,本人由于目录没配好浪费了很多时间,
声明:借用“像少年啦飞驰“这位同学的文章,在此谢过
特此发誓:只为传承,绝无他用!