加载并编译外部的 .java 文件。

huangapple go评论72阅读模式
英文:

Load and compile an external .java file

问题

以下是您要求的翻译内容:

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();
}

请注意,翻译仅限于您提供的代码部分。如果您需要更多帮助或有其他问题,请随时提问。

英文:

What is the best way to load a .java file call the main method and execute the code. So for instance if I call C:/Test.java it should execute the code.

My code right now is the following:

final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final DiagnosticCollector&lt;JavaFileObject&gt; diagnostics = new DiagnosticCollector&lt;&gt;();
final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
final Iterable&lt;&lt;? extends JavaFileObject&gt;compilationUnits = fileManager.getJavaFileObjectsFromStrings(Arrays.asList(&quot;C:/test.java&quot;));
final JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null, null, compilationUnits);

final boolean success = task.call();

try {
    System.out.println(&quot;Compiled: &quot; + success);
    fileManager.close();
} catch (final IOException e) {
    e.printStackTrace();
}

But it also generate a .class file in the folder which I don't want. Is this code good for java 14 or there is a better way?

答案1

得分: 1

首先,您可以稍微简化代码。您使用了StandardJavaFileManager,它导入了接口Auto/Closeable。因此,您可以使用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();
}

其次,您需要.class文件来执行您的Java程序。有一个很好的文章。如果您想要执行您的测试文件,并且您使用命令行,您应该使用以下命令:

javac Test.java
java Test

// 或稍微简短一些
java Test.java

如果您想了解更多关于编译的信息,我可以推荐这个文章或这个文章

英文:

First you can simplify the code a bit. You use the StandardJavaFileManager and it imports the interface Auto/Closeable. So you can use the try-with-resource:

final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
final DiagnosticCollector&lt;JavaFileObject&gt; diagnostics = new DiagnosticCollector&lt;&gt;();

try (final StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null)) {
    final Iterable&lt;&lt;? extends JavaFileObject&gt; compilationUnits = fileManager.getJavaFileObjectsFromStrings(Arrays.asList(&quot;C:/test.java&quot;));
    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. 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.

答案2

得分: 0

通过自己实现JavaFileObject并"修补" JavaFileManager,您可以将编译器保留在内存中,然后通过公开ClassLoaderdefineClass()来加载生成的字节数组,然后通过反射启动main()方法。

以下是一个精简的示例,它实际上可以执行您所要求的操作,从文件加载源代码(使用 Files.readString() 只需要一行)。然而,这实际上可能完全由修补过的 JavaFileManager 来处理,只是我专注于将源代码甚至保留在内存中,这就是为什么有注释掉的 String src = "..."; 代码块。

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"});
  }
  
  // 其他类的定义...
}

这是一个整洁、简短版本的示例,其输出如下所示:

>     public class Test {
>       public static void main(String[] args) {
>         System.out.println("Hello "+args[0]+"!");
>       }
>     }
>     true
>     Hello StackOverflow!

还有一个更长的版本,其输出较为冗长,可以在 Ideone 上找到:https://ideone.com/DAXIlH,如果您下载该版本并删除各种 System.out.println(); 前面的 //,它将显示更多信息,这些信息对 Ideone 来说太多了。

实际上,我所做的就是将所有方法实现为 throw new Error();(参见 Ideone 上的代码),然后在实际调用它们时逐渐填充它们的有意义的内容。实际上,这非常直观,唯一的问题是 JavaFileManager 接口中的一些 default 方法。由于 Eclipse 没有提供这些方法的存根实现,所以当代码在不触及我的 throw new Error(); 行时失败时,这有点令人惊讶。

英文:

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.

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). 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 = &quot;...&quot;; 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=&quot;Test&quot;;
//    String src=&quot;public class Test {\r\n&quot; + 
//        &quot;  public static void main(String[] args) {\r\n&quot; + 
//        &quot;    System.out.println(\&quot;Hello \&quot;+args[0]+\&quot;!\&quot;);\r\n&quot; + 
//        &quot;  }\r\n&quot; + 
//        &quot;}&quot;;
    String src=Files.readString(Paths.get(name+&quot;.java&quot;));
    System.out.println(src);
    List&lt;JFO&gt; sources=Arrays.asList(new JFO(name,src));
    Map&lt;String,JFO&gt; files=new HashMap&lt;&gt;();
    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&lt;?&gt; clazz=new CL().defineClass(name, files.get(name).baos.toByteArray());
    clazz.getDeclaredMethod(&quot;main&quot;, String[].class).invoke(null, (Object)new String[] {&quot;StackOverflow&quot;});
  }
  
  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&lt;String, JFO&gt; files;
    JFM(JavaFileManager jfm, Map&lt;String, JFO&gt; 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&lt;JavaFileObject&gt; list(Location location, String packageName, Set&lt;JavaFileObject.Kind&gt; 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&lt;String&gt; 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&lt;Set&lt;Location&gt;&gt; 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&lt;?&gt; 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(); calls, it will display a lot more stuff, which were simply too much for Ideone.
Practically what I did was implementing all methods as throw new Error(); (see code on Ideone), and gradually filled them out with something meaningful whenever they got actually called. Actually it was pretty straigtforward, the only gotcha I ran into was a couple default methods of the JavaFileManager interface. 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(); lines.

huangapple
  • 本文由 发表于 2020年10月2日 00:28:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/64159570.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定