目录

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

https://i-blog.csdnimg.cn/blog_migrate/d0b6d868012adfa9be3c36ed70f093e9.png

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());
        }
    }