假设我有一个包含.java文件内容的String。有没有任何API可以让我将这个源文件编译成一个虚拟的.class文件(即在内存中生成和存储内容,而不是在磁盘上创建一个实际的物理.class文件)?然后这个“虚拟”.class将在JVM中加载和执行?编辑1:我想这样做的唯一原因是,有时我的应用程序可能没有写权限。
.java
String
.class
eit6fx6z1#
使用JavaCompiler。我认为诀窍是定义一个自定义的JavaFileManager。
JavaCompiler
JavaFileManager
ux6nzvsh2#
Java确实有一个compilation API来动态编译文件,但我不知道有一个选项不会将类文件持久化到磁盘。你可以总是使用ClassLoader动态加载这些类,然后使用它们。你可以通过重写getFileForOutput方法来加载内存中的类。可选地,此文件管理器可能会将同级文件视为放置输出的提示。此提示的确切语义未指定。例如,JDK编译器javac将类文件放置在与原始源文件相同的目录中,除非提供类文件输出目录。为了促进此行为,javac可能会在调用此方法时将原始源文件作为同级文件提供。另一种选择是使用像BeanShell这样的解释器来运行java代码,它可以像代码一样执行脚本,并且可以在repl模式下工作。
ClassLoader
6ioyuze23#
javax.tools提供了您所需的一切,但需要一些技巧来停止在文件系统上存储类文件。幸运的是,它可以通过一个约100行代码的类来完成:
javax.tools
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。
3条答案
按热度按时间eit6fx6z1#
使用
JavaCompiler
。我认为诀窍是定义一个自定义的JavaFileManager
。ux6nzvsh2#
Java确实有一个compilation API来动态编译文件,但我不知道有一个选项不会将类文件持久化到磁盘。你可以总是使用
ClassLoader
动态加载这些类,然后使用它们。你可以通过重写getFileForOutput方法来加载内存中的类。可选地,此文件管理器可能会将同级文件视为放置输出的提示。此提示的确切语义未指定。例如,JDK编译器javac将类文件放置在与原始源文件相同的目录中,除非提供类文件输出目录。为了促进此行为,javac可能会在调用此方法时将原始源文件作为同级文件提供。
另一种选择是使用像BeanShell这样的解释器来运行java代码,它可以像代码一样执行脚本,并且可以在repl模式下工作。
6ioyuze23#
javax.tools
提供了您所需的一切,但需要一些技巧来停止在文件系统上存储类文件。幸运的是,它可以通过一个约100行代码的类来完成:字符集
请参阅https://gitlab.com/jcsahnwaldt/simple-java-tools。