JavaCompiler动态编译java代码

运行时将一段String流编译成一个class文件

自定义compiler

核心代码是JavaCompiler.CompilationTask, 通过调用JavaCompiler.CompilationTask.call方法

编译无误返回true,反之返回false

参数:out - 用于来自编译器的其他输出的 Writer;如果为 null,则使用 System.err

  fileManager - 文件管理器;如果为 null,则使用编译器的标准文件管理器标准文件管理器有两个用途:

    • 自定义编译器如何读写文件的基本构建块

     • 在多个编译任务之间共享

  diagnosticListener - 诊断侦听器;如果为 null,则使用编译器的默认方法报告诊断信息

  options - 编译器选项; null 表示没有选项

  classes - 类名称(用于注释处理), null 表示没有类名称

  compilationUnits - 要编译的编译单元; null 表示没有编译单元

此处我们自定了fileManager,用于接收String类型输入。编译单元是单个的String流

package io.github.kennethfan.compiler;

import javax.tools.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * Created by kenneth on 2023/5/28.
 */
public class RuntimeCompiler {

    private JavaCompiler javaCompiler;

    public RuntimeCompiler() {
        this.javaCompiler = ToolProvider.getSystemJavaCompiler();
    }

    public Class<?> compileAndLoad(String fullName, String sourceCode) throws ClassNotFoundException {
        DiagnosticCollector<? super JavaFileObject> diagnosticCollector = new DiagnosticCollector<>();
        JavaFileManager javaFileManager = new ClassFileManager(this.javaCompiler.getStandardFileManager(diagnosticCollector, null, null));

        List<JavaFileObject> javaFileObjectList = new ArrayList<>();
        javaFileObjectList.add(new JavaSourceObject(fullName, sourceCode));

        JavaCompiler.CompilationTask task = this.javaCompiler.getTask(null, javaFileManager, diagnosticCollector, null, null, javaFileObjectList);
        if (task.call()) {
            return javaFileManager.getClassLoader(null).loadClass(fullName);
        }

        System.out.println(diagnosticCollector.getDiagnostics().get(0).getLineNumber());
        System.out.println(diagnosticCollector.getDiagnostics().get(0).getColumnNumber());
        System.out.println(diagnosticCollector.getDiagnostics().get(0).getMessage(Locale.ENGLISH));
        System.out.println(diagnosticCollector.getDiagnostics().get(0).getSource());
        System.out.println(diagnosticCollector.getDiagnostics().get(0).getCode());

        return Class.forName(fullName);
    }
}

自定义源文件处理

自定义源文件处理,核心方法是getCharContent,返回源码内容,kind=SOURCE

自定义输出文件处理

接下来自定义编译class文件处理,

自定义JavaFileManager

接下来实现JavaFileManager,此处继承ForwardingJavaFileManager

测试

最后更新于