我有一个包含两个类的Java源文件:
package com.example;
public class Test {
public void sayHello() {
String hello = new HelloProducer().getHello();
System.out.println(hello);
}
public static void main(String[] args) {
new Test().sayHello();
}
}
class HelloProducer {
public String getHello() {
return "hello";
}
}
我想使用Java SDK以编程方式编译此Java源文件,这是我尝试过的。
package com.example;
import java.io.IOException;
import javax.tools.*;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
public class Compiler {
public static void main(String[] args) throws IOException {
byte[] classBytes = compile();
// how to write classBytes to two class files: HelloProducer.class and Test.class?
}
public static byte[] compile() throws IOException {
String javaSource = "package com.example;\n" +
"\n" +
"public class Test {\n" +
" public void sayHello() {\n" +
" String hello = new HelloProducer().getHello();\n" +
" System.out.println(hello);\n" +
" }\n" +
" public static void main(String[] args) {\n" +
" new Test().sayHello();\n" +
" }\n" +
"}\n" +
"class HelloProducer {\n" +
" public String getHello() {\n" +
" return \"hello\";\n" +
" }\n" +
"}\n";
GeneratedClassFile gcf = new GeneratedClassFile();
DiagnosticCollector<JavaFileObject> dc = new DiagnosticCollector<>();
GeneratedJavaSourceFile gjsf = new GeneratedJavaSourceFile("Test.java", javaSource);
JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
GeneratingJavaFileManager fileManager = new GeneratingJavaFileManager(
jc.getStandardFileManager(dc, null, null),
gcf
);
JavaCompiler.CompilationTask task = jc.getTask(null, fileManager, dc,
new ArrayList<>(), null,
Collections.singletonList(gjsf));
boolean success = task.call();
fileManager.close();
return gcf.getClassAsBytes();
}
}
class GeneratingJavaFileManager extends ForwardingJavaFileManager<JavaFileManager> {
private final GeneratedClassFile gcf;
public GeneratingJavaFileManager(
StandardJavaFileManager sjfm,
GeneratedClassFile gcf) {
super(sjfm);
this.gcf = gcf;
}
public JavaFileObject getJavaFileForOutput(
Location location, String className,
JavaFileObject.Kind kind, FileObject sibling) {
return gcf;
}
}
class GeneratedClassFile extends SimpleJavaFileObject {
private final ByteArrayOutputStream outputStream =new ByteArrayOutputStream();
public GeneratedClassFile() {
super(URI.create("generated.class"), Kind.CLASS);
}
public OutputStream openOutputStream() {
return outputStream;
}
public byte[] getClassAsBytes() {
return outputStream.toByteArray();
}
}
class GeneratedJavaSourceFile extends SimpleJavaFileObject {
private CharSequence javaSource;
public GeneratedJavaSourceFile(String fileName,
CharSequence javaSource) {
super(URI.create(fileName), Kind.SOURCE);
this.javaSource = javaSource;
}
public CharSequence getCharContent(boolean ignoreEncodeErrors) {
return javaSource;
}
}
有效。我成功地编译了此Java源代码,并获得了字节数组classBytes
。但是问题来了:使用Test.java
编译javac
时,会得到两个类文件:Test.class
和HelloProducer.class
,但是现在我只有字节数组{ {1}},如何像classBytes
一样将字节数组正确写入两个文件(Test.class
和HelloProducer.class
)?
答案 0 :(得分:0)
最后,我想出了一种方法:在生成类时只使用File
而不是OutputStream
,并且JavaCompiler
将默认为您创建两个类文件。这是一个示例:
package com.example;
import java.io.File;
import java.io.IOException;
import javax.tools.*;
import java.util.Collections;
public class Compiler {
public static void main(String[] args) throws IOException {
// source file
String[] filesToCompile = { "/path/to/input/Test.java" };
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
// output path
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, Collections.singleton(new File("/tmp")));
Iterable<? extends JavaFileObject> compilationUnits = fileManager.getJavaFileObjects(filesToCompile);
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null,null, null, compilationUnits);
boolean success = task.call();
System.out.println(success);
}
}
运行它,然后您将在/tmp/com/example
中找到两个类:HelloProducer.class
和Test.class
,这两个类都在Test.java
中定义