[英]Load and compile an external .java file
What is the best way to load a .java
file call the main method and execute the code.加载
.java
文件的最佳方法是调用 main 方法并执行代码。 So for instance if I call C:/Test.java
it should execute the code.因此,例如,如果我调用
C:/Test.java
它应该执行代码。
My code right now is the following:我现在的代码如下:
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
final Iterable<<? extends JavaFileObject>compilationUnits = fileManager.getJavaFileObjectsFromStrings(Arrays.asList("C:/test.java"));
final JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits);
final boolean success = task.call();
try {
System.out.println("Compiled: " + success);
fileManager.close();
} catch (final IOException e) {
e.printStackTrace();
}
But it also generate a .class
file in the folder which I don't want.但它也会在我不想要的文件夹中生成一个
.class
文件。 Is this code good for java 14 or there is a better way?这段代码对 java 14 有用还是有更好的方法?
First you can simplify the code a bit.首先,您可以稍微简化一下代码。 You use the StandardJavaFileManager and it imports the interface
Auto/Closeable
.您使用StandardJavaFileManager并导入接口
Auto/Closeable
。 So you can use the try-with-resource :所以你可以使用try-with-resource :
final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();
try (final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
final Iterable<<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjectsFromStrings(Arrays.asList("C:/test.java"));
final JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits);
final boolean success = task.call();
} catch (IOException e) {
e.printStackTrace();
}
Second you need the .class
file to execute your java program.其次,您需要
.class
文件来执行您的 java 程序。 There exists a nice post .有一个不错的帖子。 If you want to execute your test file and you use the command line, you should use the following commands:
如果要执行测试文件并使用命令行,则应使用以下命令:
javac Test.java
java Test
// or a little bit shorter
java Test.java
If you want to know more about compilation, I can recommend this post or this post .如果你想了解更多关于编译的知识,我可以推荐这篇文章或这篇文章。
By implementing JavaFileObject
yourself and "patching" JavaFileManager
you can keep the compiler in-memory, and then via exposing defineClass()
of ClassLoader
you can load the resulting byte array and launch main()
via reflection.通过自己实现
JavaFileObject
并“修补” JavaFileManager
您可以将编译器保留在内存中,然后通过公开ClassLoader
defineClass()
,您可以加载生成的字节数组并通过反射启动main()
。
Here is a condensed variant, it actually does what you asked for, loads source code from file (that's one line with Files.readString()
anyway).这是一个精简的变体,它实际上按照您的要求执行,从文件加载源代码
Files.readString()
无论如何,这是与Files.readString()
一行)。 However that's something what you can probably leave entirely to the patched JavaFileManager
, just I focused on keeping even the source code in memory, that's why there is the commented String src = "...";
然而,这可能完全留给打过补丁的
JavaFileManager
,只是我专注于将源代码保存在内存中,这就是为什么有注释String src = "...";
block.堵塞。
import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import javax.lang.model.element.*;
import javax.tools.*;
public class SOCompileTestDense {
public static void main(String[] args) throws Exception {
String name="Test";
// String src="public class Test {\r\n" +
// " public static void main(String[] args) {\r\n" +
// " System.out.println(\"Hello \"+args[0]+\"!\");\r\n" +
// " }\r\n" +
// "}";
String src=Files.readString(Paths.get(name+".java"));
System.out.println(src);
List<JFO> sources=Arrays.asList(new JFO(name,src));
Map<String,JFO> files=new HashMap<>();
JavaCompiler jc=ToolProvider.getSystemJavaCompiler();
JavaCompiler.CompilationTask ct=jc.getTask(null, new JFM(jc.getStandardFileManager(null, null, null),files), null, null, null, sources);
System.out.println(ct.call());
Class<?> clazz=new CL().defineClass(name, files.get(name).baos.toByteArray());
clazz.getDeclaredMethod("main", String[].class).invoke(null, (Object)new String[] {"StackOverflow"});
}
static class JFO implements JavaFileObject {
final String name;
final String src;
JFO(String name,String src){
this.name=name;
this.src=src;
}
public URI toUri() {
try {
return new URI(name);
} catch(URISyntaxException use) {use.printStackTrace();};
return null;
}
public String getName() {
return name;
}
ByteArrayOutputStream baos;
public OutputStream openOutputStream() throws IOException {
baos=new ByteArrayOutputStream();
return baos;
}
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return src;
}
public Kind getKind() {
return Kind.SOURCE;
}
public boolean isNameCompatible(String simpleName, Kind kind) {
return name.equals(simpleName);
}
public InputStream openInputStream() throws IOException {return null;}
public Reader openReader(boolean ignoreEncodingErrors) throws IOException {return null;}
public Writer openWriter() throws IOException {return null;}
public long getLastModified() {return 0;}
public boolean delete() {return false;}
public NestingKind getNestingKind() {return null;}
public Modifier getAccessLevel() {return null;}
}
static class JFM implements JavaFileManager {
final JavaFileManager jfm;
final Map<String, JFO> files;
JFM(JavaFileManager jfm, Map<String, JFO> files){
this.jfm=jfm;
this.files=files;
}
public int isSupportedOption(String option) {
return jfm.isSupportedOption(option);
}
public ClassLoader getClassLoader(Location location) {
return jfm.getClassLoader(location);
}
public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
return jfm.list(location, packageName, kinds, recurse);
}
public String inferBinaryName(Location location, JavaFileObject file) {
return jfm.inferBinaryName(location, file);
}
public boolean handleOption(String current, Iterator<String> remaining) {
return jfm.handleOption(current, remaining);
}
public boolean hasLocation(Location location) {
return jfm.hasLocation(location);
}
public JavaFileObject getJavaFileForInput(Location location, String className, JavaFileObject.Kind kind) throws IOException {
return jfm.getJavaFileForInput(location, className, kind);
}
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
JFO jfo=new JFO(className,null);
files.put(className, jfo);
return jfo;
}
public String inferModuleName(Location location) throws IOException {
return jfm.inferModuleName(location);
}
public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
return jfm.listLocationsForModules(location);
}
public boolean isSameFile(FileObject a, FileObject b) {return false;}
public FileObject getFileForInput(Location location, String packageName, String relativeName) throws IOException {return null;}
public FileObject getFileForOutput(Location location, String packageName, String relativeName, FileObject sibling) throws IOException {return null;}
public void flush() throws IOException {}
public void close() throws IOException {}
}
static class CL extends ClassLoader {
public Class<?> defineClass(String name,byte array[]) {
return defineClass(name, array, 0, array.length);
}
}
}
And this is a tidy, short version, its output looks as follows:这是一个整洁、简短的版本,其输出如下所示:
public class Test { public static void main(String[] args) { System.out.println("Hello "+args[0]+"!"); } } true Hello StackOverflow!
A much longer variant with verbose output is available on Ideone, https://ideone.com/DAXIlH , and if you download that one and remove the //
in front of various System.out.println();
Ideone 上提供了一个更长的带有详细输出的变体, https: //ideone.com/DAXIlH ,如果您下载该变体并删除各种
System.out.println();
前面的//
System.out.println();
calls, it will display a lot more stuff, which were simply too much for Ideone.调用,它会显示更多的东西,这对 Ideone 来说太多了。
Practically what I did was implementing all methods as throw new Error();
实际上我所做的是将所有方法实现为
throw new Error();
(see code on Ideone), and gradually filled them out with something meaningful whenever they got actually called. (参见 Ideone 上的代码),并在它们被实际调用时逐渐用一些有意义的东西填充它们。 Actually it was pretty straigtforward, the only gotcha I ran into was a couple
default
methods of the JavaFileManager
interface.实际上它非常简单,我遇到的唯一问题是
JavaFileManager
接口的几个default
方法。 As eclipse did not provide a stub implementation of those, it was a bit of surprise when code died without touching my throw new Error();
由于 eclipse 没有提供这些的存根实现,当代码没有触及我的
throw new Error();
死了时,这有点令人惊讶throw new Error();
lines.线。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.