在内存中编译和执行java源文件

ulydmbyx  于 2023-11-15  发布在  Java
关注(0)|答案(3)|浏览(93)

假设我有一个包含.java文件内容的String。有没有任何API可以让我将这个源文件编译成一个虚拟的.class文件(即在内存中生成和存储内容,而不是在磁盘上创建一个实际的物理.class文件)?然后这个“虚拟”.class将在JVM中加载和执行?
编辑1:我想这样做的唯一原因是,有时我的应用程序可能没有写权限。

eit6fx6z

eit6fx6z1#

使用JavaCompiler。我认为诀窍是定义一个自定义的JavaFileManager

ux6nzvsh

ux6nzvsh2#

Java确实有一个compilation API来动态编译文件,但我不知道有一个选项不会将类文件持久化到磁盘。你可以总是使用ClassLoader动态加载这些类,然后使用它们。你可以通过重写getFileForOutput方法来加载内存中的类。
可选地,此文件管理器可能会将同级文件视为放置输出的提示。此提示的确切语义未指定。例如,JDK编译器javac将类文件放置在与原始源文件相同的目录中,除非提供类文件输出目录。为了促进此行为,javac可能会在调用此方法时将原始源文件作为同级文件提供。
另一种选择是使用像BeanShell这样的解释器来运行java代码,它可以像代码一样执行脚本,并且可以在repl模式下工作。

6ioyuze2

6ioyuze23#

javax.tools提供了您所需的一切,但需要一些技巧来停止在文件系统上存储类文件。幸运的是,它可以通过一个约100行代码的类来完成:

package simple.tools;

import java.io.*;
import java.net.URI;
import java.util.*;

import javax.tools.*;
import javax.tools.JavaCompiler.CompilationTask;

public class SimpleCompiler {

    public static JavaFileObject sourceFile(String name, String source) {
        return inputFile(name, JavaFileObject.Kind.SOURCE, source);
    }

    private static URI uri(String name, JavaFileObject.Kind kind) {
        return URI.create(name.replace('.', '/') + kind.extension);
    }

    private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    private final JavaFileManager manager =
    new ForwardingJavaFileManager<JavaFileManager>(compiler.getStandardFileManager(null, null, null)) {
        @Override
        public JavaFileObject getJavaFileForOutput(Location l, String name, JavaFileObject.Kind kind, FileObject f) {
            return outputFile(name, kind);
        }
    };

    private static JavaFileObject inputFile(String name, JavaFileObject.Kind kind, String content) {
        return new SimpleJavaFileObject(uri(name, kind), kind) {
            @Override
            public CharSequence getCharContent(boolean b) {
                return content;
            }
        };
    }

    private JavaFileObject outputFile(String name, JavaFileObject.Kind kind) {
        return new SimpleJavaFileObject(uri(name, kind), kind) {
            @Override
            public OutputStream openOutputStream() {
                return outputStream(name);
            }
        };
    }

    private final Map<String, byte[]> classes = new HashMap<>();

    private OutputStream outputStream(String name) {
        return new ByteArrayOutputStream() {
            @Override
            public void close() {
                classes.put(name, toByteArray());
            }
        };
    }

    private final ClassLoader loader = new ClassLoader() {
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {
            byte[] bytes = classes.get(name);
            if (bytes == null) throw new ClassNotFoundException(name);
            return super.defineClass(name, bytes, 0, bytes.length);
        }
    };

    public Class<?> compile(String name, String source) {
        compile(sourceFile(name, source));
        try { return loadClass(name); }
        catch (ClassNotFoundException e) { throw new IllegalStateException(e.toString(), e); }
    }

    public void compile(JavaFileObject... files) {
        compile(Arrays.asList(files));
    }

    public void compile(List<JavaFileObject> files) {
        if (files.isEmpty()) throw new RuntimeException("No input files");
        DiagnosticCollector<JavaFileObject> collector = new DiagnosticCollector<>();
        CompilationTask task = compiler.getTask(null, manager, collector, null, null, files);
        boolean success = task.call();
        check(success, collector);
    }

    private void check(boolean success, DiagnosticCollector<?> collector) {
        for (Diagnostic<?> diagnostic : collector.getDiagnostics()) {
            if (diagnostic.getKind() == Diagnostic.Kind.ERROR) {
                String message = diagnostic.getMessage(Locale.US);
                throw new RuntimeException(message.split("[\r\n]")[0]);
            }
        }
        if (! success) throw new RuntimeException("Unknown error");
    }

    public Class<?> loadClass(String name) throws ClassNotFoundException {
        return loader.loadClass(name);
    }
}

字符集
请参阅https://gitlab.com/jcsahnwaldt/simple-java-tools

相关问题