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

时间:2015-05-08 00:28:57

标签: java annotations annotation-processing

我有一个注释处理器,为每个带注释的类生成一个ID类。我希望类能够在同一个编译单元中引用其他类的生成的ID类型。不幸的是,似乎注释处理器总是将生成的类作为ERROR提供,即使该类型是在先前的编译轮次中生成的,也可能是由完全独立的处理器生成的。有办法解决这个问题吗?

这是一个极小的例子。说我有以下课程:

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的类型视为ERROR,即使它是由第一个处理器生成的(并且FooId类确实存在于输出jar中)。这可以防止注释处理器分析FooId,例如,找出它所属的包以便导入它。这个问题有没有解决方法?

1 个答案:

答案 0 :(得分:4)

Java Annotation Process以轮次方式执行其工作。在每一轮中,它将运行所有适用的注释处理器,然后编译输出。 Annotation Processor API还会跟踪已处理的类,并且只处理它们一次。您遇到的问题是CreateIdProcessor正在CheckIdProcessor寻找它们的同一轮中生成类。由于在FooId运行之前未编译CheckIdProcessor,因此从调用getEnclosedElements()返回的元素是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;
  }
}