Java注解处理器在编译期自动扫描源码、生成新.java文件:javac按轮次处理,通过Elements/Types获取类型信息,用Filer输出标准源码,不修改原文件、不依赖反射,生成代码纳入编译路径并参与类型检查。

Java 注解处理器在 javac 编译阶段 扫描源码中的注解,根据预定义逻辑生成新的 .java 文件,不修改原文件,也不依赖运行时反射。
编译期触发机制
注解处理器不是手动调用的工具,而是由 javac 自动发现并执行的:
- 项目中引入了 processor 依赖(如
mapstruct-processor或自定义 jar),且该 jar 的META-INF/services/javax.annotation.processing.Processor文件里声明了处理器全限定名 - Maven 中需确保
mapstruct-processor在compile范围;Gradle 需使用annotationProcessor配置 - IDE(如 IntelliJ)必须开启 “Enable annotation processing”,否则不会运行处理器,也看不到生成的代码
- 注解本身需用
@Retention(RetentionPolicy.SOURCE)或CLASS,否则编译器在源码阶段就丢弃了,处理器无法读取
处理流程分轮次进行
javac 把编译过程拆成多轮(round),每轮只处理上一轮新生成的源文件:
使用 ImageMagick 的 convert 和 mogrify 命令进行批量图像处理,支持调整大小、裁剪、水印和格式转换。与 libvips 集成以实现高性能缩略图生成。
- 第一轮:扫描用户写的 .java 源文件,找出带目标注解(如
@Mapper、@Data)的类或接口 - 处理器的
process()方法被调用,通过RoundEnvironment获取所有被注解标记的Element(类、字段、方法等) - 根据注解元数据(如
@Mapping(source="id", target="uid"))和类型信息,用Filer写出新的 .java 文件(例如UserMapperImpl.java) - 如果生成了新源文件,javac 启动第二轮,将这些文件加入编译输入,继续扫描和处理,直到某一轮不再生成新文件为止
生成的代码在哪、怎么用
生成的 Java 源码是真实可编译的普通类,和手写代码无异:
立即学习“Java免费学习笔记(深入)”;
- Maven 默认输出到
target/generated-sources/annotations/,该路径会被自动加入编译源路径(source root) - 生成的类(如
XXXMapperImpl)实现了你声明的@Mapper接口,可以直接@Autowired或new使用 - IDE 能跳转、补全、调试这些类,因为它们是标准 Java 源码,不是字节码注入或运行时代理
- 不参与 Git 提交——它由构建过程自动生成,应被
.gitignore排除
关键支撑点:AST 与类型安全
处理器能生成正确代码,靠的是编译器提供的完整上下文:
- 通过
Elements和Types工具类,可精确获取字段类型、泛型参数、继承关系等信息 - 所有字段映射、方法调用、类型转换都基于静态类型检查,错误(如字段名拼错、类型不匹配)会在编译时报出,而非运行时崩溃
- 像 MapStruct 生成
target.setName(source.getUserName()),Lombok 生成return this.name;,都是靠解析 AST 得出的准确语义,不是字符串模板硬拼

















