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

标签 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。但问题来了:当你使用javac编译Test.java时,你会得到两个类文件:Test.classHelloProducer.class,但是现在我只有一个字节数组classBytes,如何才能像javac一样正确地将字节数组写入两个文件(Test.classHelloProducer.class) ?

最佳答案

最后,我想出了一个办法:生成类时只需使用 File 而不是 OutputStreamJavaCompiler 就会默认为你创建两个类文件。这是一个示例:

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中定义

关于java - 使用 Java SDK 以编程方式编译包含多个类的 java 源文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52584205/

相关文章:

java - Tosca Angular table 转向

java - 如何将列表的元素添加到另一个列表?

java - 从数组中获取最大整数时遇到问题

java - JDBC 驱动程序是否为 Postgres 执行自动类型转换

java - SQlite Android没有这样的表列students.studentId

java - 无法在项目 mongo-demo 上执行目标 io.thorntail :thorntail-maven-plugin:2. 2.0.Final:package (default)

java - 如何从 Android 上的 WebView 中的特定文件夹中打开 html 文件

java - 具有边框布局的 JPanel 上的事件

java - Miglayout 中不跨越拆分单元格的组件

java - 对话范围和 jsf 重定向