一、动态编译
在程序运行期间,能够动态完成源文件到字节码文件的编译。
二、目标源文件Target.java
package com.mo;
/**
* 这是一个源码文件,放在resources目录下,则是一个静态的文件
*
* @author x.pan
* @email px5215201314@163.com
* @date 2020/7/13 21:59
*/
public class Target {
/**
* 简单的一个方法
* 测试动态编译
*
* @return
*/
public String run() {
System.out.println("运行成功 ...");
return "success;";
}
}
三、Runtime
/**
* 调用Runtime进程使用javac实现动态编译
*/
@Test
public void runtime() throws Exception{
// 获取项目编译后的根据路径
URL classes = this.getClass().getResource("/");
// 获取预先定义的java文件
URL targetFile = ClassLoader.getSystemResource("Target.java");
// 1. 返回一个java相关的运行对象进程
Runtime runtime = Runtime.getRuntime();
/**
* 2. 执行javac编译命令
* -encoding 指定编码
* -cp 指定classpath,jar包中间使用;分隔
* -d 类文件的目标文件夹
*/
Process process = runtime.exec("javac -encoding UTF-8 -cp . " + targetFile.getPath() + " -d " + classes.getPath());
// 3. 当前线程等待,知道Process进程执行完
process.waitFor();
// 4. 获取进程执行结果,0 表示正常终止
if (process.exitValue() == 0) {
/**
* 加载编译后的class,并且运行
*/
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> aClass = classLoader.loadClass("com.mo.Target");
Method run = aClass.getDeclaredMethod("run");
Object o = aClass.newInstance();
run.invoke(o);
} else {
System.out.println("非正常终止");
}
}
四、JavaCompiler
/**
* 方式一
*
* 使用JavaCompiler进行编译
* <p>
* 注意:如果报java.lang.ClassNotFoundException: com.sun.tools.javac.processing.JavacProcessingEnvironment
* 原因是com.sun.tools无法加载
*/
@Test
public void tool() throws Exception {
// 获取项目编译后的根据路径
URL classes = this.getClass().getResource("/");
// 获取预先定义的java文件
URL targetFile = ClassLoader.getSystemResource("Target.java");
// 1. 获取系统的java编译器
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
// 2. 使用编译器编译文件
int exitValue = javaCompiler.run(null, null, null, "-encoding", "UTF-8", "-cp", ".", targetFile.getPath(), "-d", classes.getPath());
// 3. 获取返回结果
if (exitValue == 0) {
/**
* 加载编译后的class,并且运行
*/
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> aClass = classLoader.loadClass("com.mo.Target");
Method run = aClass.getDeclaredMethod("run");
Object o = aClass.newInstance();
run.invoke(o);
} else {
System.out.println("非正常终止");
}
}
/**
* 方式二
* 使用JavaCompiler文件系统进行编译
*
* @throws Exception
*/
@Test
public void tools() throws Exception {
// 获取项目编译后的根据路径
URL classes = this.getClass().getResource("/");
// 获取预先定义的java文件
URL targetFile = ClassLoader.getSystemResource("Target.java");
File file = new File(targetFile.toURI());
// 1. 获取JavaCompiler
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
/**
* 2. 获取文件系统
* diagnosticListener - 用于非致命诊断信息的诊断侦听器;如果为 null,则使用编译器的默认方法来报告诊断信息
* locale - 格式化诊断信息时要应用的语言环境;如果为 null,则使用默认语言环境。
* charset - 用于解码字节的字符集;如果为 null,则使用平台默认的字符集
*/
StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, null, null);
// 3. 文件管理器根与文件连接起来
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(file);
/**
* 4. 创建编译的任务
* out - 用于来自编译器的其他输出的 Writer;如果为 null,则使用 System.err
* fileManager - 文件管理器;如果为 null,则使用编译器的标准文件管理器
* diagnosticListener - 诊断侦听器;如果为 null,则使用编译器的默认方法报告诊断信息
* options - 编译器选项;null 表示没有选项
* classes - 类名称(用于注释处理),null 表示没有类名称
* compilationUnits - 要编译的编译单元;null 表示没有编译单元
*/
JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, null, Arrays.asList("-d", classes.getPath()), null, compilationUnits);
// 5. 执行编译
task.call();
fileManager.close();
/**
* 加载编译后的class,并且运行
*/
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> aClass = classLoader.loadClass("com.mo.Target");
Method run = aClass.getDeclaredMethod("run");
Object o = aClass.newInstance();
run.invoke(o);
}