Java Compiler API


一、背景与定位

1. 诞生背景

Java 6(JSR 199) 之前,Java 的编译能力主要以两种形式存在:

  1. 命令行工具javac
  2. IDE / 构建工具内部实现(如 Ant、IDEA、Eclipse 的编译器)

这些方式存在明显局限:

  • 无法在 运行时 直接进行 Java 源码编译
  • 无法在 服务端 / 工具链 / DSL / 插件系统 中嵌入标准编译能力
  • 缺乏统一、标准、可扩展的编译 API

Java Compiler API(JSR 199) 的目标是:

javac 的核心能力以 标准化、可编程 API 的形式暴露出来。


二、整体架构与核心组件

Java Compiler API 主要位于:

复制代码
javax.tools

其整体结构可以理解为一个 可嵌入式编译器框架

![[1_1Java Compiler API.png]]

![[1_2Java Compiler API.png]]

1. 核心接口总览

接口 / 类 作用
JavaCompiler 编译器主入口
ToolProvider 获取系统编译器
CompilationTask 一次编译任务
JavaFileObject 抽象的"源码/字节码文件"
JavaFileManager 文件管理与输出控制
StandardJavaFileManager 默认文件系统实现
Diagnostic / DiagnosticCollector 编译错误、警告信息
FileObject 更通用的文件抽象

三、核心接口详解

1. ToolProvider & JavaCompiler

java 复制代码
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
关键说明
  • 仅在 JDK 中可用

  • 如果运行环境是 JRE / jlink runtime ,该方法返回 null

  • 本质上获取的是 javac 的程序化入口


2. JavaFileObject:源码与字节码的统一抽象

java 复制代码
public interface JavaFileObject extends FileObject

它抽象了:

  • .java 源码
  • .class 字节码
  • 内存中的"虚拟文件"
常见实现
  • SimpleJavaFileObject
  • StandardJavaFileManager 内部实现

这也是 "内存编译""动态代码生成" 的基础。


3. JavaFileManager:编译输入 / 输出控制中心

负责:

  • 源文件的查找
  • classpath / module-path 的解析
  • 编译产物(.class)的输出位置
java 复制代码
StandardJavaFileManager fm =
    compiler.getStandardFileManager(diagnostics, null, null);
高级用法
  • 自定义 JavaFileManager
  • .class 输出到:
    • 内存
    • 数据库
    • 网络
    • 自定义 ClassLoader

4. CompilationTask:一次完整的编译任务

java 复制代码
JavaCompiler.CompilationTask task =
    compiler.getTask(
        writer,
        fileManager,
        diagnostics,
        options,
        classes,
        compilationUnits
    );
参数语义
参数 含义
writer 编译器输出(stdout/stderr)
fileManager 文件管理
diagnostics 错误收集
options -classpath-source
classes 注解处理目标类
compilationUnits 待编译源码

5. Diagnostic:编译错误的结构化表示

java 复制代码
for (Diagnostic<?> d : diagnostics.getDiagnostics()) {
    System.out.println(d.getKind());
    System.out.println(d.getMessage(null));
}

可获取:

  • 错误 / 警告 / 提示
  • 行号、列号
  • 源文件
  • 错误码

这使得 IDE、代码分析器、在线判题系统 成为可能。


四、最小可运行示例

1. 编译磁盘上的 Java 文件

java 复制代码
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<>();

StandardJavaFileManager fileManager =
        compiler.getStandardFileManager(diagnostics, null, null);

Iterable<? extends JavaFileObject> units =
        fileManager.getJavaFileObjectsFromStrings(
                List.of("HelloWorld.java"));

boolean success = compiler.getTask(
        null,
        fileManager,
        diagnostics,
        null,
        null,
        units
).call();

2. 内存编译(无文件系统)

核心思路:

  • 自定义 SimpleJavaFileObject
  • String 存源码
  • ByteArrayOutputStream.class

这是 脚本引擎 / DSL / 规则引擎 的基础能力。


五、与 Annotation Processing(JSR 269)的关系

Java Compiler API 与 注解处理器 深度集成:

text 复制代码
javac
 ├─ Parse
 ├─ Enter
 ├─ Annotation Processing  ← APT
 ├─ Attr
 ├─ Flow
 ├─ Desugar
 └─ Generate

典型应用

  • Lombok
  • MapStruct
  • AutoValue
  • Dagger
  • 自研代码生成器

你可以通过 Compiler API 显式启用 / 控制注解处理器


六、典型应用场景

1. 代码分析 / 静态检查

  • 自研 Sonar 规则引擎
  • 编译级 AST 校验
  • API 兼容性检查

2. 动态代码执行

  • 在线判题系统
  • 规则引擎
  • 表达式语言(EL / DSL)

3. 插件系统

  • 热加载业务逻辑
  • 用户自定义扩展
  • 脚本替代 Groovy / JS

4. IDE / 工具链

  • 语法检查
  • 实时错误提示
  • 编译日志可视化

七、与 Roslyn(C#)的对比

维度 Java Compiler API Roslyn
AST 可操作性 中等 极强
API 现代性 一般 非常好
编译即服务 支持 原生支持
增量编译
语法树改写 困难 一等公民

Java Compiler API 偏"编译控制"

Roslyn 偏"编译平台"


八、局限性与注意事项

  1. 不是官方 Script Engine
  2. API 偏底层,使用成本高
  3. AST 操作不如 Roslyn 直观
  4. 模块化(JPMS)后 classpath 配置更复杂
  5. 不适合频繁高并发动态编译(需缓存 ClassLoader)

九、总结一句话

Java Compiler API 是"把 javac 变成库"的官方标准方案,是构建 Java 代码分析、动态编译、DSL、工具链与平台级能力的基础设施。

相关推荐
2401_8769075217 小时前
USB TYPE-C 公头连接器设计规范总结:提升可靠性、降本增效的关键指南
c语言·开发语言·设计规范
额呃呃17 小时前
std::allocator<T>::destroy
开发语言
不知疲倦的仄仄17 小时前
第二天:深入理解 Selector:单线程高效管理多个 Channel
java·nio
期待のcode17 小时前
Java虚拟机栈
java·开发语言·jvm
珂朵莉MM17 小时前
全球校园人工智能算法精英大赛-产业命题赛-算法巅峰赛 2025年度画像
java·人工智能·算法·机器人
芒克芒克17 小时前
本地部署SpringBoot项目
java·spring boot·spring
cute_ming18 小时前
关于基于nodeMap重构DOM的最佳实践
java·javascript·重构
sww_102618 小时前
Netty原理分析
java·网络
小突突突18 小时前
Spring框架中的单例bean是线程安全的吗?
java·后端·spring
iso少年18 小时前
Go 语言并发编程核心与用法
开发语言·后端·golang