mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
786 字
2 分钟
Java注解处理器详解
2026-02-02

说实话,刚接触注解处理器的时候我是有点懵的——编译时还能跑代码生成代码?后来在项目里用了一回,自动生成Builder类和Mapper,那种”原来还能这么玩”的感觉是真上头。这篇文章就聊聊我踩过的坑和总结下来的经验。

Java注解处理器详解#

什么是注解处理器?#

注解处理器(Annotation Processor)就是Java编译器的一个插件,在编译的时候扫描和处理注解,自动生成代码。Lombok的@Getter、@Setter就是靠这个实现的,刚知道的时候我还在想”这玩意儿是魔法吧”。

工作原理#

  1. 编译器扫描源码,收集所有注解
  2. 把注解传给对应的处理器
  3. 处理器处理注解,生成新的源码或资源文件
  4. 编译器继续编译生成的代码

创建注解处理器的步骤#

1. 定义注解#

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.SOURCE)
public @interface GenerateBuilder {
}

2. 实现处理器#

继承AbstractProcessor,重写process方法:

@SupportedAnnotationTypes("com.example.GenerateBuilder")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class BuilderProcessor extends AbstractProcessor {
private Filer filer;
private Messager messager;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
for (TypeElement annotation : annotations) {
for (Element element : roundEnv.getElementsAnnotatedWith(annotation)) {
if (element instanceof TypeElement) {
generateBuilder((TypeElement) element);
}
}
}
return true;
}
private void generateBuilder(TypeElement typeElement) {
// 生成Builder类的代码
// 用filer.createSourceFile()创建新文件
}
}

3. 注册处理器#

META-INF/services/javax.annotation.processing.Processor文件中写入处理器的全限定类名。

或者用Google的auto-service库,加个@AutoService(Processor.class)注解就行,省得手动写配置文件。

核心API#

  • ProcessingEnvironment:处理器与编译器的通信渠道
    • getFiler():创建新文件
    • getMessager():输出日志/错误信息
    • getElementUtils():操作元素(类、方法、字段等)
    • getTypeUtils():操作类型
  • RoundEnvironment:当前轮次的信息
    • getElementsAnnotatedWith():获取被注解标记的元素

生成代码的示例#

生成Builder类的核心逻辑就是用Filer创建Java源文件,然后用字符串拼接的方式写入代码。虽然看起来有点原始,但这就是注解处理器的标准做法。

JavaFileObject javaFileObject = filer.createSourceFile(packageName + "." + builderClassName);
try (Writer writer = javaFileObject.openWriter()) {
writer.write("package " + packageName + ";\n\n");
writer.write("public class " + builderClassName + " {\n");
// ... 生成字段、setter方法、build方法
writer.write("}\n");
}

注意事项#

  1. 避免无限循环——如果处理器生成的代码也包含被处理的注解,就会陷入死循环。这个坑我真踩过——生成代码里忘了去掉注解,结果编译的时候CPU飙到100%,我还以为是电脑中毒了
  2. 跨模块处理——处理器只能处理当前模块的代码,不能处理其他模块的
  3. 调试困难——在编译时运行,没法直接打断点。可以用Messager输出日志来调试
  4. 多轮处理——处理器可能会被调用多次,因为生成的代码也需要被处理

替代方案#

  • Lombok:最流行的注解处理器,减少样板代码
  • JavaParser:解析和修改Java源代码
  • ByteBuddy/ASM:运行时生成和修改字节码

💡 实战贴士: 如果你只是想减少样板代码(getter/setter/toString),直接用Lombok就够了,别自己写注解处理器。注解处理器的真正价值在于生成重复的业务代码,比如根据实体类自动生成Mapper接口、Builder类等。这种场景下自己写一个处理器,能省下大量重复劳动。

分享

如果这篇文章对你有帮助,欢迎分享给更多人!

部分信息可能已经过时

目录