JAVA动态编译执行文本代码和加载执行jar包

一、动态编译器实现类

package com.api.utils;
import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 动态编译器实现类
 *
 * @author zhanqi
 * @since 2020-11-21 13:39:34
 *
 */
public class DynamicConditionCompiler {
    //类全名
    private String fullClassName;
    private String sourceCode;
    //存放编译之后的字节码
    private Map<String, ByteJavaFileObject> javaFileObjectMap = new ConcurrentHashMap<>();
    //获取java的编译器
    private JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    //存放编译过程中输出的信息
    private DiagnosticCollector<JavaFileObject> diagnosticsCollector = new DiagnosticCollector<>();
    //执行结果(控制台输出的内容)
    private String runResult;
    //编译耗时(单位ms)
    private long compilerTakeTime;
    //运行耗时(单位ms)
    private long runTakeTime;


    public DynamicConditionCompiler(String sourceCode) {
        this.sourceCode = sourceCode;
        this.fullClassName = getFullClassName(sourceCode);
    }

    /**
     * 编译字符串源代码,编译失败在 diagnosticsCollector 中获取提示信息
     *
     * @return true:编译成功 false:编译失败
     */
    public boolean compiler() {
        long startTime = System.currentTimeMillis();
        //标准的内容管理器,更换成自己的实现,覆盖部分方法
        StandardJavaFileManager standardFileManager = compiler.getStandardFileManager(diagnosticsCollector, null, null);
        JavaFileManager javaFileManager = new StringJavaFileManage(standardFileManager);
        //构造源代码对象
        JavaFileObject javaFileObject = new StringJavaFileObject(fullClassName, sourceCode);
        //获取一个编译任务
        JavaCompiler.CompilationTask task = compiler.getTask(null, javaFileManager, diagnosticsCollector, null, null, Arrays.asList(javaFileObject));
        //设置编译耗时
        compilerTakeTime = System.currentTimeMillis() - startTime;
        return task.call();
    }

    /**
     * 执行main方法,重定向System.out.print
     */
    public void runMainMethod(String methodName,Object... pramas) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, UnsupportedEncodingException {
        PrintStream out = System.out;
        try {
            long startTime = System.currentTimeMillis();
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            PrintStream printStream = new PrintStream(outputStream);
            //PrintStream PrintStream = new PrintStream("/Users/temp.sql"); //输出到文件
            System.setOut(printStream);

            StringClassLoader scl = new StringClassLoader();
            Class<?> aClass = scl.findClass(fullClassName);
            Method method;
            //invoke无参方法
            if(pramas==null){
                //调用main方法
                if(methodName.equals("main")){
                    method = aClass.getDeclaredMethod("main", String[].class);
                    Object[] pars = new Object[]{1};
                    pars[0] = new String[]{};
                    method.invoke(null, pars);
                }else {
                    method = aClass.getDeclaredMethod(methodName);
                    method.invoke(aClass.newInstance());
                }
             //invoke有参方法
            }else{
                Class<?>[] argTypes=new Class<?>[pramas.length];
                for(int i=0;i<pramas.length;i++){
                    Object obj = pramas[i];
                    if(obj instanceof Class<?>){
                        argTypes[i]=Class.class;
                    }else if(obj instanceof String){
                        argTypes[i]=String.class;
                    }else if(obj instanceof Double){
                        argTypes[i]=Double.class;
                    }else if(obj instanceof Integer){
                        argTypes[i]=Integer.class;
                    }else if(obj instanceof Boolean){
                        argTypes[i]=Boolean.class;
                    }else if(obj instanceof List){
                        argTypes[i]=List.class;
                    }else if(obj instanceof Map){
                        argTypes[i]=Map.class;
                    }else if(obj instanceof ArrayList){
                        argTypes[i]=ArrayList.class;
                    }else if(obj instanceof LinkedHashMap){
                        argTypes[i]=LinkedHashMap.class;
                    }else if(obj instanceof LinkedList){
                        argTypes[i]=LinkedHashMap.class;
                    }else if(obj instanceof LinkedHashSet){
                        argTypes[i]=LinkedHashSet.class;
                    }else if(obj instanceof HashSet){
                        argTypes[i]=HashSet.class;
                    }else{
                        argTypes[i]=Object.class;
                    }

                }
                method = aClass.getDeclaredMethod(methodName,argTypes);
                method.invoke(aClass.newInstance(),pramas);

            }
            //设置运行耗时
            runTakeTime = System.currentTimeMillis() - startTime;
            //设置打印输出的内容
            runResult = new String(outputStream.toByteArray(), "utf-8");
        } catch (InstantiationException e) {
            e.printStackTrace();
        } finally {
            //还原默认打印的对象
            System.setOut(out);
        }

    }

    /**
     * @return 编译信息(错误 警告)
     */
    public String getCompilerMessage() {
        StringBuilder sb = new StringBuilder();
        List<Diagnostic<? extends JavaFileObject>> diagnostics = diagnosticsCollector.getDiagnostics();
        for (Diagnostic diagnostic : diagnostics) {
            sb.append(diagnostic.toString()).append("\r\n");
        }
        return sb.toString();
    }

    /**
     * @return 控制台打印的信息
     */
    public String getRunResult() {
        return runResult;
    }

    public long getCompilerTakeTime() {
        return compilerTakeTime;
    }

    public long getRunTakeTime() {
        return runTakeTime;
    }

    /**
     * 获取类的全名称
     *
     * @param sourceCode 源码
     * @return 类的全名称
     */
    public static String getFullClassName(String sourceCode) {
        String className = "";
        Pattern pattern = Pattern.compile("package\\s+\\S+\\s*;");
        Matcher matcher = pattern.matcher(sourceCode);
        if (matcher.find()) {
            className = matcher.group().replaceFirst("package", "").replace(";", "").trim() + ".";
        }
        pattern = Pattern.compile("class\\s+\\S+\\s+\\{");
        matcher = pattern.matcher(sourceCode);
        if (matcher.find()) {
            className += matcher.group().replaceFirst("class", "").replace("{", "").trim();
        }
        return className;
    }

    /**
     * 自定义一个字符串的源码对象
     */
    private class StringJavaFileObject extends SimpleJavaFileObject {
        //等待编译的源码字段
        private String contents;

        //java源代码 => StringJavaFileObject对象 的时候使用
        public StringJavaFileObject(String className, String contents) {
            super(URI.create("string:///" + className.replaceAll("\\.", "/") + Kind.SOURCE.extension), Kind.SOURCE);
            this.contents = contents;
        }

        //字符串源码会调用该方法
        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
            return contents;
        }

    }

    /**
     * 自定义一个编译之后的字节码对象
     */
    private class ByteJavaFileObject extends SimpleJavaFileObject {
        //存放编译后的字节码
        private ByteArrayOutputStream outPutStream;
        public ByteJavaFileObject(String className, Kind kind) {
            super(URI.create("string:///" + className.replaceAll("\\.", "/") + Kind.SOURCE.extension), kind);
        }
        //StringJavaFileManage 编译之后的字节码输出会调用该方法(把字节码输出到outputStream)
        @Override
        public OutputStream openOutputStream() {
            outPutStream = new ByteArrayOutputStream();
            return outPutStream;
        }

        //在类加载器加载的时候需要用到
        public byte[] getCompiledBytes() {
            return outPutStream.toByteArray();
        }
    }

    /**
     * 自定义一个JavaFileManage来控制编译之后字节码的输出位置
     */
    private class StringJavaFileManage extends ForwardingJavaFileManager {
        StringJavaFileManage(JavaFileManager fileManager) {
            super(fileManager);
        }

        //获取输出的文件对象,它表示给定位置处指定类型的指定类。
        @Override
        public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
            ByteJavaFileObject javaFileObject = new ByteJavaFileObject(className, kind);
            javaFileObjectMap.put(className, javaFileObject);
            return javaFileObject;
        }
    }

    /**
     * 自定义类加载器, 用来加载动态的字节码
     */
    private class StringClassLoader extends ClassLoader {
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            ByteJavaFileObject fileObject = javaFileObjectMap.get(name);
            if (fileObject != null) {
                byte[] bytes = fileObject.getCompiledBytes();
                return defineClass(name, bytes, 0, bytes.length);
            }
            try {
                return ClassLoader.getSystemClassLoader().loadClass(name);
            } catch (Exception e) {
                return super.findClass(name);
            }
        }
    }

    /**
     * 动态加载jar包
     * @param jarPath
     */
    public  void loadJar(String jarPath) {
        File jarFile = new File(jarPath);
        // 从URLClassLoader类中获取类所在文件夹的方法,jar也可以认为是一个文件夹
        Method method = null;
        try {
            method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
        } catch (NoSuchMethodException | SecurityException e1) {
            e1.printStackTrace();
        }
        // 获取方法的访问权限以便写回
        boolean accessible = method.isAccessible();
        try {
            method.setAccessible(true);
            // 获取系统类加载器
            URLClassLoader classLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
            URL url = jarFile.toURI().toURL();
            method.invoke(classLoader, url);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            method.setAccessible(accessible);
        }
    }
}

二、实体类用于测试List<?>

package com.api.entity;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;


/**
 * 维表实体类
 */
@Data
@ApiModel
public class DimInfo implements Serializable {

    @ApiModelProperty(value = "查询出来的值")
    private Map<String, Object> valueMap;

    @ApiModelProperty(value = "sql参数")
    private Map<String, Object[]> sqlParams;

}

三、测试运行

package com.api;

import com.api.entity.DimInfo;
import com.api.utils.DynamicConditionCompiler;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 动态编译器运行
 *
 * @author zhanqi
 * @since 2020-11-21 13:46:12
 */
public class DynamicConditionTest {
    public static void main(String[] args) {
        String code = " import com.api.entity.DimInfo; " +
                " import java.util.*;" +
                "   public class HelloWorld {\n" +
                "      public  void myMethod(Integer j, List<DimInfo> list){\n" +
                "        System.out.println(\"j->   \"+j);\n" +
                "        System.out.println(\"list->     \"+list);\n" +
                "       }" +
                "   }";
        DynamicConditionCompiler compiler = new DynamicConditionCompiler(code);
        boolean res = compiler.compiler();
        if (res) {
            System.out.println("编译成功");
            System.out.println("编译时间:" + compiler.getCompilerTakeTime());
            try {
                List<DimInfo> list = new ArrayList<>();
                DimInfo dimInfo = new DimInfo();
                Map<String, Object> map = new HashMap<>();
                map.put("key", "詹啟");
                dimInfo.setValueMap(map);
                list.add(dimInfo);
                compiler.runMainMethod("myMethod", 10, list);
                System.out.println("运行时间:" + compiler.getRunTakeTime());
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(compiler.getRunResult());
            System.out.println("错误|警告信息:" + compiler.getCompilerMessage());
        } else {
            System.out.println("编译失败");
            System.out.println(compiler.getCompilerMessage());
        }

    }
}

四、运行结果

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值