Silentdoer

导航

动态编译和加载java代码

package me.silentdoer.dynamicgeneration;

import javax.tools.JavaCompiler;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * if IDE is idea, change working dir to xxx/target/classes
 *
 * @author liqi.wang
 * @version 1.0.0
 * @since 2020/9/28 15:05
 */
public class Entrance {
    /**
     * package's folder must exists
     */
    public static String inputCode = "package dynamic.generation.mock; public class UserFuck" +
            " { public static void test(){System.out.println(\"Hello444\");}}";

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException
            , IllegalAccessException, InstantiationException, InvocationTargetException {

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager manager = compiler.getStandardFileManager(null, Locale.CHINA, StandardCharsets.UTF_8);
        // dynamic/generation is the based folder for generated code package
        File file = new File("dynamic/generation");
        if (file.exists() && file.isFile()) {
            throw new IllegalStateException("package folder is a file");
        } else if (!file.exists()) {
            boolean flag = file.mkdirs();
            if (!flag) {
                throw new IllegalStateException("create package's folder failure.");
            }
        }
        // can cache value, and if exists .class, ignore
        String className = parseClassName(inputCode);
        StringSourceJavaObject sourceObject = new StringSourceJavaObject(className, Entrance.inputCode);
        List<StringSourceJavaObject> objects = Collections.singletonList(sourceObject);
        // define output dir
        List<String> options = Arrays.asList("-d", "./");
        JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, options, null, objects);
        Boolean compilerResult = task.call();
        if (compilerResult) {
            System.out.println("compile success");
            ClassLoader classLoader = Entrance.class.getClassLoader();
            // this throws ClassNotFoundException
            Class<?> userFuck = classLoader.loadClass(className);
            Method test = userFuck.getMethod("test");
            test.invoke(userFuck.newInstance());
        }
    }

    static String parseClassName(String inputCode) {
        Pattern packagePattern = Pattern.compile("package\\s+(\\S+);\\s*");
        Matcher packageMatcher = packagePattern.matcher(inputCode);
        if (!packageMatcher.find()) {
            throw new IllegalArgumentException("input code is invalid.");
        }
        String packageName = packageMatcher.group(1);
        System.out.println("package: " + packageName);
        Pattern classSimpleNamePatter = Pattern.compile("public\\s+class\\s+(\\w+)\\s+\\{");
        Matcher classSimpleNameMatcher = classSimpleNamePatter.matcher(inputCode);
        if (!classSimpleNameMatcher.find()) {
            throw new IllegalArgumentException("input code is invalid.");
        }
        String classSimpleName = classSimpleNameMatcher.group(1);
        System.out.println("className: " + classSimpleName);
        return packageName + "." + classSimpleName;
    }

    static class StringSourceJavaObject extends SimpleJavaFileObject {

        private final String sourceCode;

        public StringSourceJavaObject(String className, String sourceCode) {
            // DynamicCodeGen.UserFuck . to /
            super(URI.create("string:///" + className.replace(".", "/") + Kind.SOURCE.extension), Kind.SOURCE);
            this.sourceCode = sourceCode;
        }

        @Override
        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return this.sourceCode;
        }
    }
}

 

posted on 2020-09-29 16:47  Silentdoer  阅读(504)  评论(0编辑  收藏  举报