一、注解处理器是什么?

如果你用过 Lombok,肯定对 @Data 这种注解不陌生——加上它就能自动生成 getter/setter,不用手动写重复代码。这种"魔法"背后的核心技术就是注解处理器(Annotation Processor)。简单来说,它是 Java 编译时的一个钩子,能扫描代码中的注解并生成新代码。

举个例子,假设你定义了一个注解 @Builder,希望用它自动生成建造者模式代码。注解处理器会在编译阶段读取这个注解,然后帮你生成 UserBuilder.java 这样的类。整个过程完全自动化,就像有个小助手在帮你写代码。

二、手把手实现一个注解处理器

环境准备(技术栈:Java + Maven)

首先创建一个 Maven 项目,添加依赖:

<dependencies>
    <!-- 注解处理器的核心依赖 -->
    <dependency>
        <groupId>com.google.auto.service</groupId>
        <artifactId>auto-service</artifactId>
        <version>1.0</version>
    </dependency>
</dependencies>

第一步:定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE) // 只能用在类上
@Retention(RetentionPolicy.SOURCE) // 源码级别保留
public @interface AutoToString {
    String prefix() default "[DEBUG]"; // 可配置参数
}

第二步:实现处理器逻辑

import com.google.auto.service.AutoService;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.TypeElement;
import java.util.Set;

@AutoService(Processor.class) // 自动注册处理器
@SupportedAnnotationTypes("com.example.AutoToString") // 处理的注解类型
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class AutoToStringProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
        annotations.forEach(annotation -> {
            // 找到所有被@AutoToString标记的类
            env.getElementsAnnotatedWith(annotation).forEach(element -> {
                String className = element.getSimpleName().toString();
                String prefix = element.getAnnotation(AutoToString.class).prefix();
                // 生成toString()方法代码
                String code = String.format(
                    "public String toString() { return \"%s\" + super.toString(); }", 
                    prefix
                );
                // 这里实际应该用JavaPoet等库生成完整类,简化示例
                System.out.println("生成代码: " + className + " -> " + code);
            });
        });
        return true;
    }
}

第三步:实际使用

@AutoToString(prefix = "用户信息:")
public class User {
    private String name;
    private int age;
}

// 编译后会自动生成:
// public String toString() { return "用户信息:" + super.toString(); }

三、高级技巧:用JavaPoet优雅生成代码

直接拼接字符串生成代码很痛苦,推荐使用 Square 的 JavaPoet

// 添加Maven依赖
<dependency>
    <groupId>com.squareup</groupId>
    <artifactId>javapoet</artifactId>
    <version>1.13.0</version>
</dependency>

// 修改处理器代码
MethodSpec toStringMethod = MethodSpec.methodBuilder("toString")
    .addAnnotation(Override.class)
    .returns(String.class)
    .addStatement("return $S + super.toString()", prefix)
    .build();

TypeSpec builderClass = TypeSpec.classBuilder(className + "ToStringHelper")
    .addModifiers(Modifier.PUBLIC)
    .addMethod(toStringMethod)
    .build();

// 写入文件
JavaFile.builder("com.generated", builderClass).build().writeTo(processingEnv.getFiler());

四、应用场景与实战建议

典型应用场景

  1. 减少样板代码:比如自动生成 Builder、DTO 转换器
  2. 编译时检查:验证注解使用是否合规(例如 @NonNull
  3. 生成框架代码:类似 Retrofit 的动态代理类生成

技术优缺点

优点

  • 性能零开销(运行时不需要反射)
  • 代码可读性高(生成的代码可直接查看)

缺点

  • 学习曲线较陡峭
  • 调试困难(错误通常在编译时暴露)

避坑指南

  1. 不要修改已有代码:注解处理器只能生成新文件
  2. 注意编译顺序:处理器本身需要先编译
  3. 增量处理:覆盖 getSupportedOptions() 支持增量编译

五、总结

注解处理器是 Java 中一块隐藏的宝藏,用好了能极大提升开发效率。虽然前期需要投入时间学习,但一旦掌握,你就会发现它能解决很多重复劳动问题。建议从小型实验项目开始,逐步应用到生产环境中。