在Java中读取custom annotation processor的代码时,
我在处理器的process
方法中注意到了这段代码:
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (!roundEnv.errorRaised() && !roundEnv.processingOver()) {
processRound(annotations, roundEnv);
}
return false;
}
碰巧我正在开发一个自定义Annotation处理器,&amp;我想在我的注释处理器中使用上面的代码段。
我用这种方式尝试了代码:
if (!roundEnv.errorRaised() && !roundEnv.processingOver()) {
processRound(annotations, roundEnv);
}
return false;
&安培;这样:
if (!roundEnv.errorRaised()) {
processRound(annotations, roundEnv);
}
return false;
但我没有注意到处理器行为的任何变化。
我得到!roundEnv.errorRaised()
支票,但我看不出!roundEnv.processingOver()
如何有用。
我想知道在处理某一轮时使用roundEnv.processingOver()
非常有用的用例。
答案 0 :(得分:16)
这两项检查都很重要,但在同一项目中同时运行多个注释处理器之前,您不会注意到它们的影响。让我解释一下。
当Javac因任何原因(例如由于缺少类型声明或解析错误)而导致编译失败时,它不会立即终止。相反,它将尽可能多地收集有关错误的信息,并尝试以有意义的方式向用户显示该信息。此外,如果有注释处理器,并且错误是由缺少类型或方法声明引起的,Javac将尝试运行这些处理器并重试编译,希望它们生成缺少的代码。这被称为&#34;多轮编译&#34;。
编译序列如下所示:
每轮都是编译代码的全面尝试。除最后一轮之外的每一轮都将重新运行代码上的每个注释处理器,这些处理器先前由注释处理器生成。
这个精彩的序列允许使用像Dagger2和Android-Annotated-SQL这样的库推广的方法:在源代码中引用尚未存在的类,并让注释处理器在编译期间生成它:
// this would fail with compilation error in absence of Dagger2
// but annotation processor will generate the Dagger_DependencyFactory
// class during compilation
Dagger_DependencyFactory.inject(this);
有些人认为这种技术是不合理的,因为它依赖于在源代码中使用不存在的类,并且将源代码与注释处理紧密联系起来(并且在IDE代码完成时不能很好地工作)。但这种做法本身是合法的,并且按照Javac开发人员的意图运作。
那么,在你的问题中,所有这些与Spring的注释处理器有什么关系呢?
TL; DR:你问题中的代码是错误的。
使用这些方法的正确方式如下:
代表errorRaised
:
errorRaised
。这可以确保您在Javac进行错误报告狂欢时尽可能少地遗漏这些东西。代表errorRaised
:
processingOver
返回false),请尝试生成尽可能多的输出;忽略用户代码中缺少的类型和方法(假设,其他一些注释处理器可能会在后续轮次中生成它们)。但是仍然尽可能地生成,以防其他注释处理器可能需要它。例如,如果在每个类上触发代码生成(使用processingOver
注释),则应迭代这些类并尝试为每个类生成代码,即使先前的类有错误或缺少方法也是如此。就个人而言,我只是在try-catch中包装每个单独的生成单元,并检查@Entity
:如果它是假的,忽略错误并继续迭代注释并生成代码。这使得Javac能够破坏代码之间的循环依赖关系,这些代理由不同的注释处理器生成,直到完全满意为止。processingOver
返回false),并且之前的一些注释未被处理(我会在处理因异常而失败时记住它们),重试处理那些。processingOver
返回true),请查看是否仍有未处理的注释。如果是这样,则编译失败(仅在最后轮期间!)上面的序列是使用processingOver
的预期方式。
某些注释处理器稍微使用processingOver
:它们缓冲每轮中生成的代码,并在上一轮中实际将其写入processingOver
。这允许解析其他处理器的依赖性,但是阻止其他处理器查找由&#34;小心&#34;生成的代码。处理器。这是一个有点讨厌的策略,但如果生成的代码不打算在其他地方引用,我想这没关系。
还有像上面提到的第三方Spring配置验证器这样的注释处理器:它们误解了一些东西,并以猴子和扳手的方式使用API。
为了更好地了解整个事物,请安装Dagger2,并尝试在类中引用Dagger生成的类,由另一个注释处理器使用(最好以某种方式使该处理器解析它们)。这将很快向您展示这些处理器如何应对多轮编译。大多数人只会让Javac崩溃。有些人会吐出数千个错误,填充IDE错误报告缓冲区并混淆编译结果。很少有人能够正确参与多轮编译,但如果失败则仍然会发出很多错误。
尽管存在错误,仍然会生成代码&#34; part特别用于减少编译失败期间报告的编译错误数。减少缺少类=减少缺少声明错误(希望如此)。或者,不要创建注释处理器,以煽动用户引用它们生成的代码。但是,当一些注释处理器生成代码时,您还需要处理情况,并使用您的注释进行注释 - 与#34;提前不同&#34;声明,用户将期望只是开箱即用。
回到原始问题:由于Spring配置验证处理器不会生成任何代码(希望我没有深入研究它),但应该始终报告扫描配置中的所有错误,理想情况下应该像这样工作:忽略Filer
并推迟配置扫描,直到errorRaised
返回true:这将避免在多个编译轮次期间多次报告相同的错误,并允许注释处理器生成新的配置片段。
可悲的是,有问题的处理器看起来已经放弃了(自2015年以来没有提交过),但是作者在Github上是活跃的,所以也许你可以向他们报告这个问题。
与此同时,我建议您学习经过深思熟虑的注释处理器,例如Google Auto,Dagger2或my tiny research project。