使用Java SDK以编程方式编译包含多个类的Java源文件

时间:2018-10-01 03:26:28

标签: java

我有一个包含两个类的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.classHelloProducer.class,但是现在我只有字节数组{ {1}},如何像classBytes一样将字节数组正确写入两个文件(Test.classHelloProducer.class)?

1 个答案:

答案 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.classTest.class,这两个类都在Test.java中定义