bigywater.tech
Java · 226

注解处理器:在编译期生成代码与诊断

注解处理器在编译期读取源码结构、生成代码、报出诊断。这篇真实写一个只有几十行的处理器,逐项验证它的轮次、生成文件、诊断位置,以及处理器被发现的几种方式。

3 轮
处理器被调用的轮数(含最后一轮 processingOver)
0 个 class
处理器报 ERROR 后 -d 目录里的 class 数
Note
JDK 21 在 classpath 上发现处理器、未显式启用时打印的提示

生成源文件

实测 · 本机执行输出
  javac rc = 0
      [processor] 结束,本处理器共生成 [PersonBuilder]
    Note: 为 Person 生成 PersonBuilder(2 个字段)
  -s 目录里的生成文件: ['gen1/demo/PersonBuilder.java'] ;-d 里的 class: ['Main.class', 'Person.class', 'PersonBuilder.class']
  生成的 PersonBuilder.java:
    package demo;
    public class PersonBuilder {
        private java.lang.String name;
        public PersonBuilder name(java.lang.String v) { this.name = v; return this; }
        private int age;
        public PersonBuilder age(int v) { this.age = v; return this; }
        public Person build() { return new Person(name, age); }
    }
  Ann/30
  javap 看 Person.class 是否还带 @AutoBuilder(RetentionPolicy.SOURCE): False

处理轮次

实测 · 本机执行输出
  [processor] 第 1 轮:annotations=[AutoBuilder],根元素 [Person, Main],processingOver=false,errorRaised=false
  [processor] 第 2 轮:annotations=[],根元素 [PersonBuilder],processingOver=false,errorRaised=false
  [processor] 第 3 轮:annotations=[],根元素 [],processingOver=true,errorRaised=false
  [processor] 结束,本处理器共生成 [PersonBuilder]
Note: 为 Person 生成 PersonBuilder(2 个字段)

诊断与 -Werror

实测 · 本机执行输出
  正常:javac rc = 0
      [processor] 结束,本处理器共生成 [PersonBuilder, EmptyBuilder]
    Note: 为 Person 生成 PersonBuilder(2 个字段)
    app/demo/Empty.java:1: warning: Empty 没有实例字段,生成的 Builder 是空的
    package demo; import ap.AutoBuilder; @AutoBuilder public class Empty { public Empty() {} }
                                                             ^
    Note: 为 Empty 生成 EmptyBuilder(0 个字段)
    1 warning
  加 -Aap.fail=1:javac rc = 1
      [processor] 结束,本处理器共生成 [PersonBuilder]
    app/demo/Person.java:1: error: Person 被配置为失败(-Aap.fail)
    package demo; import ap.AutoBuilder; @AutoBuilder public class Person { final String name; final int age; public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return name + "/" + age; } }
                                                             ^
    Note: 为 Person 生成 PersonBuilder(2 个字段)
    1 error
  失败时 -d 下的 class 数: 0 ;生成的 PersonBuilder.java 在 -s 目录里: True
  加 -Werror(处理器发了 WARNING):javac rc = 1 ; ['error: warnings found and -Werror specified']

处理器的发现方式

实测 · 本机执行输出
  classpath 上有 services 文件、什么选项都不加(JDK 21):rc=0,生成源文件 ['PersonBuilder.java'],class ['Main.class', 'Person.class', 'PersonBuilder.class']
        [processor] 结束,本处理器共生成 [PersonBuilder]
      Note: Annotation processing is enabled because one or more processors were found
        on the class path. A future release of javac may disable annotation processing
        unless at least one processor is specified by name (-processor), or a search
        path is specified (--processor-path, --processor-module-path), or annotation
        processing is enabled explicitly (-proc:only, -proc:full).
        Use -Xlint:-options to suppress this message.
        Use -proc:none to disable annotation processing.
      Note: 为 Person 生成 PersonBuilder(2 个字段)
    javac 输出: ['  [processor] 结束,本处理器共生成 [PersonBuilder]', 'Note: Annotation processing is enabled because one or more processors were found', '  on the class path. A future release of javac may disable annotation processing']
  加 -proc:none:rc=1,生成源文件 [],class ['Person.class']
      app/demo/Main.java:1: error: cannot find symbol
      package demo; public class Main { public static void main(String[] a) { System.out.println("  " + new PersonBuilder().name("Ann").age(30).build()); } }
                                                                                                            ^
        symbol:   class PersonBuilder
        location: class Main
      1 error
  加 -proc:full:rc=0,生成源文件 ['PersonBuilder.java'],class ['Main.class', 'Person.class', 'PersonBuilder.class']
        [processor] 结束,本处理器共生成 [PersonBuilder]
      Note: 为 Person 生成 PersonBuilder(2 个字段)
    javac 输出: ['  [processor] 结束,本处理器共生成 [PersonBuilder]', 'Note: 为 Person 生成 PersonBuilder(2 个字段)']
  -proc:only(只跑处理器,不生成 class):rc=0,生成源文件 ['PersonBuilder.java'],class []
        [processor] 结束,本处理器共生成 [PersonBuilder]
      Note: 为 Person 生成 PersonBuilder(2 个字段)
  用 -processorpath 指定处理器、classpath 上没有处理器:rc=0,生成源文件 ['PersonBuilder.java'],class ['Main.class', 'Person.class', 'PersonBuilder.class']
        [processor] 结束,本处理器共生成 [PersonBuilder]
      Note: 为 Person 生成 PersonBuilder(2 个字段)

分步演示

注解处理器
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12 的 javac
    • 没有验证:Maven 与 Gradle 中的处理器配置(annotationProcessorPaths、增量处理)、Lombok 与 MapStruct 等第三方处理器、处理器里的类型镜像 API 的更多用法(TypeMirror、Types)、多个处理器之间的顺序、IDE 内的处理器行为
    京ICP备2026067502号-1京公网安备11010502063487号