注释处理器是否可以访问上一轮生成的类型?

Is it possible for an annotation processor to access types generated in a previous round?

我有一个注释处理器,它为每个注释 class 生成一个 ID class。我希望 classes 能够引用同一编译单元中其他 classes 生成的 ID 类型。不幸的是,注释处理器似乎总是将生成的 class 类型给出为 ERROR,即使该类型是在前一轮编译中生成的或由完全独立的处理器生成的。有解决办法吗?

这是一个最小的例子。假设我有以下 class:

package tmp;

@MyAnnotation
public class Foo {
  private Foo me;
  private FooId myId;
}

它首先由这个注释处理器处理以生成 ID:

@SupportedAnnotationTypes("tmp.proc.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class CreateIdProcessor extends AbstractProcessor {
  @Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    for (Element elem : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) {
      System.out.println("Writing ID for " + elem.getSimpleName());
      try {
        Writer file = processingEnv.getFiler().createSourceFile(elem + "Id").openWriter();
        file.write(String.format("package tmp; public class %sId {}", elem.getSimpleName()));
        file.close();
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
    }
    return false;
  }
}

然后由这个注释处理器处理以分析类型:

@SupportedAnnotationTypes("tmp.proc.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class CheckIdProcessor extends AbstractProcessor {
  @Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    for (Element elem : roundEnv.getElementsAnnotatedWith(MyAnnotation.class)) {
      for (Element element : elem.getEnclosedElements()) {
        System.out.printf("%s = %s (%s)\n", element.getSimpleName(), element.asType(), element.asType().getKind());
      }
    }
    return false;
  }
}

运行 构建的输出如下所示:

Writing ID for Foo
<init> = ()void (EXECUTABLE)
me = tmp.Foo (DECLARED)
myId = FooId (ERROR)

一切编译正常,但第二个处理器将 myId 的类型视为错误,即使它是由第一个处理器生成的(并且 FooId class 确实存在于输出 jar 中) .这可以防止注释处理器分析 FooId 以找出它所属的包以便导入它。这个问题有解决办法吗?

Java 注释进程轮流执行其工作。在每一轮中,它将 运行 所有适用的注释处理器,然后编译输出。 Annotation Processor API 还会跟踪已处理的内容 类,并且只处理一次。您 运行 遇到的问题是您的 CreateIdProcessorCheckIdProcessor 正在寻找它们的同一轮中生成 类。因为 FooId 没有在 CheckIdProcessor 运行 之前编译,调用 getEnclosedElements() 返回的 Element 是 ERROR 元素。

简而言之,您的第二个处理器将 FooId 字段类型视为错误,因为 FooId 的源已生成,但尚未编译。

这是一个你可以做的实验 运行。此注释处理器将打印出在给定回合中处理的所有新 类。 FooId 出现在 Round: 1 下,而 Foo 会出现在 Round: 0 下:

Round: 0
Foo = tmp.Foo (DECLARED)
<init> = ()void (EXECUTABLE)
me = tmp.Foo (DECLARED)
myId = FooId (ERROR)
Writing ID for Foo
Round: 1
FooId = tmp.FooId (DECLARED)
Round: 2
Round: 3

这是我为此使用的注释处理器:

@SupportedAnnotationTypes("*")
@SupportedSourceVersion(SourceVersion.RELEASE_6)
public class OutputRoundProcessor extends AbstractProcessor {
  private int round = 0;
  @Override
  public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    System.out.println("Round: " + round);
    for (Element element : roundEnv.getRootElements()) {
      System.out.printf("%s = %s (%s)\n", element.getSimpleName(), element.asType(), element.asType().getKind());
    }
    round++;
    return false;
  }
}