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

问题描述 投票:0回答:3

假设我有一个

String
,其中包含
.java
文件的内容。是否有任何 API 允许我将此源文件编译为虚拟
.class
文件(即生成内容并将其存储在内存中,而不是在磁盘上创建实际的物理
.class
文件)?然后这个“虚拟”
.class
就会在JVM中加载并执行?

编辑1:我想这样做的唯一原因是因为有时,我的应用程序可能没有写入权限。

java compilation javac .class-file
3个回答
1
投票

为此请使用

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


1
投票

Java 确实有一个 compilation API 来动态编译文件,但我不知道有一个选项不会将类文件保存到磁盘。您始终可以使用

ClassLoader
并动态加载这些类,然后使用它们。您可以通过重写 getFileForOutput 方法来加载内存中的类。

可选地,该文件管理器可能会将同级视为 在哪里放置输出。该提示的确切语义是 未指定。 JDK 编译器,例如 javac,将放置类 文件与原始源文件位于同一目录中,除非 提供了class文件输出目录。为了促进这种行为, javac 可能会在以下情况下提供原始源文件作为同级文件 调用这个方法。

另一种选择是使用像 BeanShell 这样的解释器来为您运行 java 代码。它像代码一样执行脚本,并且可以在 repl 模式下工作。


0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.