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、工具链与平台级能力的基础设施。

相关推荐
非优秀程序员4 小时前
第04章 邮箱大战,163 与 QQ 的暗战|《互联网产品恩仇录》
java·vue.js·架构
知已难寻4 小时前
Docker 入门:容器技术与 Docker 基础
java·运维·服务器·docker·云原生·容器
沐晓时光5 小时前
C语言入门,内存函数知识讲解
c语言·开发语言
文丑颜不良啊6 小时前
LeetCode2481~2500题目解析
java·leetcode
Flynn Liang7 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope
聪明的笨猪猪8 小时前
实际工程问题:AI 生成 Spring Cache(@Cacheable / @CachePut / @CacheEvict)缓存注解踩坑
java·经验分享·面试·ai编程
愿有所息8 小时前
记一次 ARM64 + JDK8 上 fastjson2 的诡异数据错乱
java
秋饼9 小时前
MCP 2026-07-28 无状态协议大改版:Spring AI 2.1 多轮往返请求与网关路由落地
java·ai·技术分享·后端开发
SL_staff10 小时前
JVS-BI 实战:业务人员如何用零代码 ELT + 可视化配置构建实时经营分析闭环
java·spring·全栈
SL_staff10 小时前
企业AI落地难?JVS-AI渐进式实践:安全可控、小切口验证、标准化沉淀与规模化复制
java·开源·aigc