js css htmljavajava高级开发

Java动态编译

2022-12-29  本文已影响0人  老鼠AI大米_Java全栈

写工具时遇到一个需求,程序跑起来之后,可以在程序上写代码并编译执行,这种情况就用到了Java动态编译运行

流程

获取JavaCompiler

获取JavaCompiler需要用到jdk的tools包,如果只有jre,就需要手动把tools包放到JAVA_HOME的lib目录下

private static JavaCompiler compiler;

static {
    compiler = ToolProvider.getSystemJavaCompiler();
    if (compiler == null) {
        String error = String.format("Java运行环境缺少文件:请将'系统jdk目录\\lib\\tools.jar'文件复制到'%s\\lib\\目录下'", System.getProperty("java.home"));
        System.out.println("ClassUtil init: " + error);
        throw new RuntimeException(error);
    }
}

编译文件

调用JavaCompiler的run方法,即可编译Java文件,run方法接收一个输入流,两个输出流和若干个字符串参数,源码注释如下:

/**
 * Run the tool with the given I/O channels and arguments. By
 * convention a tool returns 0 for success and nonzero for errors.
 * Any diagnostics generated will be written to either {@code out}
 * or {@code err} in some unspecified format.
 *
 * @param in "standard" input; use System.in if null
 * @param out "standard" output; use System.out if null
 * @param err "standard" error; use System.err if null
 * @param arguments arguments to pass to the tool
 * @return 0 for success; nonzero otherwise
 * @throws NullPointerException if the array of arguments contains
 * any {@code null} elements.
 */
int run(InputStream in, OutputStream out, OutputStream err, String... arguments);

加载class文件

上面的编译步骤完成后,会输出class文件,想要将class运行起来,要先加载文件
使用ClassLoader来加载class文件

//编译的类文件路径
public static final String CLASS_PATH = System.getProperty("user.dir") + File.separator + "target" + File.separator + "classes";

static class MyClassLoader extends ClassLoader { 
    
    @Override
    protected Class<?> findClass(String name) {
        String myPath = "file:///" + CLASS_PATH.replaceAll("\\\\", "/") + "/" + name.replace(".", "/") + ".class";
        byte[] cLassBytes = null;
        try {
            Path path = Paths.get(new URI(myPath));
            cLassBytes = Files.readAllBytes(path);
        } catch (IOException | URISyntaxException e) {
            System.out.println(e);
        }
        return defineClass(name, cLassBytes, 0, cLassBytes.length);
    }
}

private static Class<?> load(String name) {
    //加载类文件的方法,返回加载后的Class
    Class<?> cls = null;
    try {
        //这里使用自定义的ClassLoader
        ClassLoader classLoader = new MyClassLoader();
    } catch (Exception e) {
        System.out.println(e);
    }
    return cls;
}

使用加载后的类

将类文件加载后,就可以使用了,通过反射机制,获取类的方法并调用

/**
 * 调用类方法
 *
 * @param cls        类
 * @param methodName 方法名
 * @param paramsCls  方法参数类型
 * @param params     方法参数
 * @return
 */
public static Object invoke(Class<?> cls, String methodName, Class<?>[] paramsCls, Object[] params)
        throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
    Method method = cls.getDeclaredMethod(methodName, paramsCls);
    Object obj = cls.newInstance();
    return method.invoke(obj, params);
}

public static void main(String[] args) {
    Class<?> cls = load("com.xxx.xxx");
    //调用加载后Class的方法
    invoke(cls, methodName, paramsCls, params);
}

完整代码

Logger是自定义的日志类

package com.xxx.utils;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class ClassUtil {
    public static final String CLASS_PATH = System.getProperty("user.dir") + File.separator + "target" + File.separator + "classes";
    private static JavaCompiler compiler;

    static {
        compiler = ToolProvider.getSystemJavaCompiler();
        if (compiler == null) {
            String error = String.format("Java运行环境缺少文件:请将'系统jdk目录\\lib\\tools.jar'文件复制到'%s\\lib\\目录下'", System.getProperty("java.home"));
            Logger.e("ClassUtil init", error);
            throw new RuntimeException(error);
        }
    }

    static class MyClassLoader extends ClassLoader {
        @Override
        protected Class<?> findClass(String name) {
            String myPath = "file:///" + CLASS_PATH.replaceAll("\\\\", "/") +
                    "/" + name.replace(".", "/") + ".class";
            byte[] cLassBytes = null;
            try {
                Path path = Paths.get(new URI(myPath));
                cLassBytes = Files.readAllBytes(path);
            } catch (IOException | URISyntaxException e) {
                Logger.e(e);
            }
            return defineClass(name, cLassBytes, 0, cLassBytes.length);
        }
    }

    public static Object execute(ExecuteOptions options)
            throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        Logger.i("java file path: " + options.compilerOptions.srcPath);
        compiler(options.compilerOptions);
        Class<?> cls = load(String.format("%s.%s", options.pkgName, options.clzName));
        return invoke(cls, options.methodName, options.paramsCls, options.params);
    }

    public static int compiler(CompilerOptions options) {
        checkCompiler();
        return compiler.run(options.in, options.out, options.error, "-d", options.targetPath, options.srcPath);
    }

    /**
     * 加载类
     *
     * @param name 类名
     * @return
     */
    private static Class<?> load(String name) {
        Class<?> cls = null;
        try {
            ClassLoader classLoader = new MyClassLoader();
            //classLoader = ClassUtil.class.getClassLoader();
            cls = classLoader.loadClass(name);
            Logger.d("Load Class[" + name + "] by " + classLoader);
        } catch (Exception e) {
            Logger.e(e);
        }
        return cls;
    }

    /**
     * 调用类方法
     *
     * @param cls        类
     * @param methodName 方法名
     * @param paramsCls  方法参数类型
     * @param params     方法参数
     * @return
     */
    public static Object invoke(Class<?> cls, String methodName, Class<?>[] paramsCls, Object[] params)
            throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Method method = cls.getDeclaredMethod(methodName, paramsCls);
        Object obj = cls.newInstance();
        return method.invoke(obj, params);
    }

    private static void checkCompiler() {
        if (compiler == null) {
            compiler = ToolProvider.getSystemJavaCompiler();
            if (compiler == null) {
                String error = String.format("Java运行环境缺少文件:请将'系统jdk目录\\lib\\tools.jar'文件复制到'%s\\lib\\目录下'", System.getProperty("java.home"));
                Logger.e("ClassUtil init", error);
                throw new RuntimeException(error);
            }
        }
    }

    /**
     * 执行参数
     */
    public static class ExecuteOptions{
        public CompilerOptions compilerOptions;
        public String pkgName;
        public String clzName;
        public String methodName;
        public Class<?>[] paramsCls;
        public Object[] params;

        public ExecuteOptions() {
            super();
        }

        public ExecuteOptions(CompilerOptions compilerOptions, String pkgName, String clzName, String methodName, Class<?>[] paramsCls, Object[] params) {
            this.compilerOptions = compilerOptions;
            this.pkgName = pkgName;
            this.clzName = clzName;
            this.methodName = methodName;
            this.paramsCls = paramsCls;
            this.params = params;
        }

        public ExecuteOptions setCompilerOptions(CompilerOptions compilerOptions) {
            this.compilerOptions = compilerOptions;
            return this;
        }

        public ExecuteOptions setPkgName(String pkgName) {
            this.pkgName = pkgName;
            return this;
        }

        public ExecuteOptions setClzName(String clzName) {
            this.clzName = clzName;
            return this;
        }

        public ExecuteOptions setMethodName(String methodName) {
            this.methodName = methodName;
            return this;
        }

        public ExecuteOptions setParamsCls(Class<?>[] paramsCls) {
            this.paramsCls = paramsCls;
            return this;
        }

        public ExecuteOptions setParams(Object[] params) {
            this.params = params;
            return this;
        }
    }

    /**
     * 编译参数
     */
    public static class CompilerOptions {
        public InputStream in;
        public OutputStream out;
        public OutputStream error;
        public String targetPath = CLASS_PATH;
        public String srcPath;

        public CompilerOptions() {
            super();
        }

        public CompilerOptions(String targetPath, String srcPath) {
            this.targetPath = targetPath;
            this.srcPath = srcPath;
        }

        public CompilerOptions(InputStream in, OutputStream out, OutputStream error, String targetPath, String srcPath) {
            this.in = in;
            this.out = out;
            this.error = error;
            this.targetPath = targetPath;
            this.srcPath = srcPath;
        }

        public CompilerOptions setIn(InputStream in) {
            this.in = in;
            return this;
        }

        public CompilerOptions setOut(OutputStream out) {
            this.out = out;
            return this;
        }

        public CompilerOptions setError(OutputStream error) {
            this.error = error;
            return this;
        }

        public CompilerOptions setTargetPath(String targetPath) {
            this.targetPath = targetPath;
            return this;
        }

        public CompilerOptions setSrcPath(String srcPath) {
            this.srcPath = srcPath;
            return this;
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读