java-动态编译运行
目录
java 动态编译运行
文章目录
写在前面
Java Compiler 可以动态执行一段字符串形式的java代码,或手动编译java文件。
1、Java Compiler
1.1、java文件编译成class文件
run 编译
D:\Test.java 文件内容:
public class Test {
public static void main(String[] args) {
int i = 0;
System.out.println("test run "+i);
}
}
把java文件编译成class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int result = compiler.run(null, null, null, "D:\\Test.java"); // 在 D:\\Test.java 同目录下,会出现Test.class文件
System.out.println(result == 0 ? "编译成功" : "编译失败");
exec 执行指令
exec 执行指令,等同于在cmd中输入指令
// 执行java命令,空参数,所在文件夹
Process process = Runtime.getRuntime().exec("java Test", null, new File("F:\\demo\\"));
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String str;
while ((str = bufferedReader.readLine()) != null) {
System.out.println(str); // 这里打印的是控制台输出的
}
如上代码,等同于在cmd输入:java Test
1.2、字符串编译成class文件
新建类 StringObject,代表字符串java对象内容
package com.kaka;
import javax.tools.SimpleJavaFileObject;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
// 必须继承自SimpleJavaFileObject
public class StringObject extends SimpleJavaFileObject {
private String content = null;
protected StringObject(String className, String contents) throws URISyntaxException {
super(new URI(className), Kind.SOURCE);
this.content = contents;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return content;
}
}
编译java字符串
package com.kaka;
import javax.tools.*;
import java.net.URISyntaxException;
import java.util.Collections;
public class Test01 {
public static void main(String[] args) throws URISyntaxException {
// 要编译的字符串,这个字符串必须是一个类
String contents = "package com.kaka;" +
"class Test {\n" +
" public static void main(String[] args) {\n" +
"\tint i = 0;\n" +
" System.out.println(\"测试运行 \"+i);\n" +
" }\n" +
"}";
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager standardJavaFileManager = javaCompiler.getStandardFileManager(null, null, null);
JavaFileObject testFile = new StringObject("com.kaka.Test", contents);
JavaCompiler.CompilationTask task = javaCompiler.getTask(null, standardJavaFileManager, null, null, null, Collections.singletonList(testFile));
if(task.call()){
System.out.println("success");
}else{
System.out.println("failure!");
}
}
}
1.3、工具类
将1.1和1.2总结为一个工具类
package com;
import javax.tools.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class JavaCodeUtil {
/** java字符串代码,转为class文件 **/
public static String codeToClass(String code,String compilePath) {
String className = getClassNameFromCode(code);
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, null, null);
JavaFileObject stringObject = new StringObject(className, code);
// compilePath 指定编译的class文件的路径
List<String> options = Arrays.asList("-d", compilePath);
JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, null, options, null, Collections.singletonList(stringObject));
if(task.call()){
// 从code第一行读出包名
String packageName = getPackageNameInFirstLine(code);
String classFileName = className + "." + "class";
return compilePath + "/" + packageName + "/" + classFileName;
}else{
return null;
}
}
public static String javaToClass(String fileName,String compilePath) throws IOException {
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = javaCompiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> fileObjects = fileManager.getJavaFileObjects(fileName);
// compilePath 指定编译的class文件的路径
List<String> options = Arrays.asList("-d", compilePath);
JavaCompiler.CompilationTask task = javaCompiler.getTask(null, fileManager, null, options, null, fileObjects);
if(task.call()){
// 从文件第一行读包名
File sourceFile = new File(fileName);
BufferedReader bufferedReader = new BufferedReader(new FileReader(sourceFile));
String line;
while ((line = bufferedReader.readLine()) != null) {
if (line.startsWith("package")) {
break;
}
}
String packageName = line;
packageName = packageName.replaceAll("package", "").trim().replaceAll("\", "/");
packageName = packageName.substring(0, packageName.length() - 1);
// 获取类名(这里简单处理, 默认文件名与类名相同)
String sourceFileName = sourceFile.getName();
String[] split = sourceFileName.split("\");
String simpleClassName = split[0] + "." + "class";
return compilePath + "/" + packageName + "/" + simpleClassName;
}else{
return null;
}
}
public static class StringObject extends SimpleJavaFileObject {
private final String content;
public StringObject(String className, String contents) {
super(URI.create(className), Kind.SOURCE);
this.content = contents;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors){
return content;
}
}
/** 根据java文件第一行获取包名 */
public static String getPackageNameInFirstLine(String code){
return code
.substring(0,code.indexOf(";"))
.replace("package ", "")
.replaceAll("\", "/");
}
/** 从java String字符串中获取类名 */
public static String getClassNameFromCode(String code){
int classBlank = code.indexOf("class ")+6;
String className = code.substring(classBlank);
int nextBlank = className.indexOf(" ");
className = className.substring(0,nextBlank);
if(className.contains("{")){
int i = className.indexOf("{");
className = className.substring(0,i);
}
return className;
}
/** 获取项目根目录 */
public static String getProjectPath() {
String path = JavaCodeUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath();
if (System.getProperty("os.name").contains("dows")) {
path = path.substring(1);
}
if (path.contains("jar")) {
path = path.substring(0, path.lastIndexOf("."))
.substring(0, path.lastIndexOf("/"));
return path;
} else {
return path.replace("target/classes/", "");
}
}
}
测试使用JavaCodeUtil
public static void main(String[] args) throws ClassNotFoundException {
// java代码
String contents = "package com.kaka;\n" +
"class Test02 {\n" +
" public static void main(String[] args) {\n" +
"\tint i = 0;\n" +
" System.out.println(\"这是Test02的main方法 \"+i);\n" +
" }\n" +
" public void getiii(){}\n" +
"}";
// 把字符串编译成class文件
JavaCodeUtil.codeToClass(contents,getProjectPath()+"target/classes");
// 加载这个calss文件
Class<?> aClass = Class.forName("com.kaka.Test02");
// 获取Test02中的方法
Method[] methods = aClass.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
}