You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建可自行生成注解并加以使用的Java注解处理器?

如何创建可自行生成注解并加以使用的Java注解处理器?

咱们这个需求核心是要让注解处理器干两件事:先根据标记生成枚举专属注解,再能识别并处理自己刚生成的注解,而且得搞定编译阶段的轮次问题,不然编译器根本不认你刚生成的代码。下面我一步步给你拆解怎么实现:

一、先搭好触发生成的“入口”注解

首先得定义一个用来触发注解生成的初始注解,比如@GenerateAnnotation,专门标记在枚举上,告诉处理器“给这个枚举生成专属注解”。代码这么写:

@Retention(RetentionPolicy.SOURCE) // 只在编译阶段生效,不用保留到运行时
@Target(ElementType.TYPE) // 能标记在类、枚举这些类型上
public @interface GenerateAnnotation {
}

二、实现第一轮处理:生成枚举专属注解

接下来写注解处理器的核心逻辑,先处理@GenerateAnnotation标记的枚举,生成对应的专属注解。

1. 处理器基础骨架

先写个继承AbstractProcessor的处理器类,加上必要的注解让编译器识别它:

@SupportedAnnotationTypes("com.yourpackage.GenerateAnnotation") // 指定要处理的初始注解
@SupportedSourceVersion(SourceVersion.RELEASE_8) // 适配你用的Java版本
public class EnumAnnotationProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        // 核心处理逻辑写在这
        return false; // 重点!返回false表示还要继续处理后续轮次,不能返回true
    }
}

这里一定要返回false,因为我们生成新代码后,需要编译器触发下一轮处理,返回true的话编译就直接结束了,新生成的注解没人管。

2. 扫描枚举并生成专属注解

在process方法里,扫描所有被@GenerateAnnotation标记的枚举,然后生成对应的注解。推荐用JavaPoet来生成代码,比手动拼字符串靠谱多了,能避免很多语法错误。

举个JavaPoet的例子,生成MyEnum对应的MyEnumAnnotation:

// 遍历所有被@GenerateAnnotation标记的元素
for (Element element : roundEnv.getElementsAnnotatedWith(GenerateAnnotation.class)) {
    // 先判断是不是枚举,不是的话抛编译错误
    if (element.getKind() != ElementKind.ENUM) {
        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
                "@GenerateAnnotation只能标记在枚举类上!", element);
        continue;
    }

    TypeElement enumElement = (TypeElement) element;
    String enumName = enumElement.getSimpleName().toString();
    String annotationName = enumName + "Annotation";
    String packageName = processingEnv.getElementUtils().getPackageOf(enumElement).getQualifiedName().toString();

    // 用JavaPoet构建注解类
    TypeSpec annotationSpec = TypeSpec.annotationBuilder(annotationName)
            .addModifiers(Modifier.PUBLIC)
            // 给生成的注解加元注解,比如保留到编译阶段
            .addAnnotation(AnnotationSpec.builder(Retention.class)
                    .addMember("value", "$T.SOURCE", RetentionPolicy.class)
                    .build())
            // 定义注解的value属性,类型就是当前枚举
            .addMethod(MethodSpec.methodBuilder("value")
                    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                    .returns(ClassName.get(enumElement))
                    .build())
            .build();

    // 生成Java文件,写到编译系统的输出目录
    JavaFile javaFile = JavaFile.builder(packageName, annotationSpec).build();
    try {
        javaFile.writeTo(processingEnv.getFiler());
    } catch (IOException e) {
        processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR,
                "生成注解失败:" + e.getMessage(), element);
    }
}

这段代码干的事就是:找到标记的枚举,生成一个以枚举名+Annotation命名的注解,注解的value类型就是这个枚举本身,还自动加了元注解。

三、实现第二轮处理:识别并使用自己生成的注解

现在处理器已经能生成注解了,接下来要让它能处理这些生成的注解,比如用户在其他类上用了@MyEnumAnnotation(MyEnum.TEST),我们要能扫描到并生成业务代码。

1. 让处理器支持多轮扫描

首先,把处理器的@SupportedAnnotationTypes改成通配符*,这样它能扫描到所有注解(包括新生成的),或者你也可以在process方法里动态判断注解的命名规则(比如以Annotation结尾的)。

然后,在process方法里,除了处理@GenerateAnnotation,还要加一段逻辑扫描所有自定义的枚举专属注解:

// 处理第一轮生成的枚举专属注解
for (TypeElement annotationType : roundEnv.getRootElements().stream()
        .filter(e -> e.getKind() == ElementKind.ANNOTATION_TYPE)
        .map(e -> (TypeElement) e)
        .filter(e -> e.getSimpleName().toString().endsWith("Annotation"))
        .toList()) {
    // 扫描所有使用这个注解的元素
    Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotationType);
    for (Element elem : annotatedElements) {
        // 这里写你要做的逻辑,比如生成重复代码
        // 举个例子,生成一个toString方法或者其他业务代码
        generateBoilerplateCode(elem, annotationType);
    }
}

这样,当第一轮生成注解后,编译器会触发第二轮处理,这时候处理器就能扫描到新生成的注解,以及使用它的代码,然后做后续处理。

2. 注意编译轮次的细节

  • 每一轮处理后,编译器会检查有没有新生成的源文件,如果有就触发下一轮,直到没有新文件为止。所以你的处理器要能正确处理多轮,不要在第一轮就把所有逻辑都做完。
  • 生成的注解如果需要在运行时使用,记得把元注解的Retention改成RUNTIME,但如果只是编译阶段用,SOURCE就够了,更高效。

四、打包和使用处理器

最后要把处理器打包成jar,并且在jar里加上META-INF/services/javax.annotation.processing.Processor文件,文件内容是你的处理器的全类名,比如com.yourpackage.EnumAnnotationProcessor,这样编译器(比如javac、IDEA的编译器)才能自动发现这个处理器。

使用的时候,只要把这个jar加到项目依赖里,然后:

  1. 定义枚举并标记@GenerateAnnotation;
  2. 在其他类上用生成的@MyEnumAnnotation;
  3. 编译项目,处理器会自动完成两轮处理,生成你需要的代码。

备注:内容来源于stack exchange,提问作者cancech

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 11:58:08