gpt4 book ai didi

java - 注解处理,RoundEnvironment.processingOver()

转载 作者:行者123 更新时间:2023-12-02 07:25:53 26 4
gpt4 key购买 nike

在阅读 custom annotation processor 的代码时在 java ,
我在处理器的 process 中注意到了这段代码方法:

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (!roundEnv.errorRaised() && !roundEnv.processingOver()) {
processRound(annotations, roundEnv);
}
return false;
}

碰巧我也在使用自定义注释处理器,并且我想在我的注释处理器中使用上面的代码片段。

我以这种方式尝试了上面的代码:
if (!roundEnv.errorRaised() && !roundEnv.processingOver()) {
processRound(annotations, roundEnv);
}
return false;

&这样:
if (!roundEnv.errorRaised()) {
processRound(annotations, roundEnv);
}
return false;

但我没有注意到处理器行为的任何变化。
我收到了 !roundEnv.errorRaised()检查,但我看不到 !roundEnv.processingOver()任何有用的。

我想知道使用 roundEnv.processingOver() 有用的用例在处理某一轮时。

最佳答案

这两项检查都很重要,但只有在同一项目中同时运行多个注释处理器时,您才会注意到它们的影响。让我解释一下。

当 Javac 由于任何原因(例如,由于缺少类型声明或解析错误)导致编译失败时,它不会立即终止。相反,它将尽可能多地收集有关错误的信息,并尝试以有意义的方式向用户显示该信息。此外,如果有注解处理器,并且错误是由于缺少类型或方法声明引起的,Javac 将尝试运行这些处理器并重试编译,希望它们生成丢失的代码。这称为“多轮编译”。

编译序列将如下所示:

  • 初级轮次(可能与代码生成);
  • 几个可选的代码生成轮;新的轮次将发生,直到注释处理器没有生成任何内容;
  • 最后一轮;本轮生成的代码不会经过注解处理。

  • 每一轮都是编译代码的全面尝试。除了最后一轮之外,每一轮都会重新运行之前由注释处理器生成的代码上的每个注释处理器。

    这个奇妙的序列允许使用由 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 继续进行错误报告时,您会留下尽可能少的遗漏。
  • 如果您的代码没有生成新的公开可见的类(例如,因为它只创建包私有(private)类,而其他代码会在运行时反射性地查找它们,请参阅 ButterKnife),那么您应该检查 errorRaised尽快,如果返回 true 则立即退出。这将简化您的代码并加速错误编译。

  • processingOver :
  • 如果当前回合不是最后一次(processingOver 返回 false),请尝试生成尽可能多的输出;忽略用户代码中缺少的类型和方法(假设其他注释处理器可能会在接下来的几轮中生成它们)。但仍然尽量生成,以防其他注释处理器可能需要它。例如,如果您在每个类上触发代码生成,注释为 @Entity ,您应该迭代这些类并尝试为每个类生成代码,即使以前的类有错误或缺少方法。就个人而言,我只是将每个单独的生成单元包装在 try-catch 中,并检查 processingOver : 如果为 false,则忽略错误并继续迭代注释并生成代码。这允许 Javac 打破由不同注释处理器生成的代码之间的循环依赖,通过运行它们直到完全满意。
  • 如果当前轮不是最后一轮(processingOver 返回false),并且前一轮的一些注释没有被处理(每当处理因异常失败时我都会记住它们),重试处理这些注释。
  • 如果当前回合是最后一次(processingOver 返回 true),查看是否还有未处理的注释。如果是这样,编译失败(仅在最后一轮!)

  • 上面的顺序是使用 processingOver 的预期方式.

    一些注释处理器使用 processingOver有点不同:它们缓冲每一轮生成的代码,并将其实际写入 Filer在最后一轮。这允许解决对其他处理器的依赖性,但阻止其他处理器找到“谨慎”处理器生成的代码。这是一个有点讨厌的策略,但如果生成的代码不打算在其他地方引用,我想这没问题。

    还有像上面提到的第三方Spring配置 validator 这样的注解处理器:他们误解了一些东西,用猴子和 Spanner 的风格来使用API​​。

    为了更好地了解整个事情,安装 Dagger2,并尝试在类中引用 Dagger 生成的类,由另一个注释处理器使用(最好以某种方式使该处理器解析它们)。这将很快向您展示这些处理器如何处理多轮编译。除了异常(exception),大多数只会使 Javac 崩溃。有些会吐出数千个错误,填充 IDE 错误报告缓冲区并混淆编译结果。很少有人会正确地参与多轮编译,但如果失败仍然会吐出很多错误。

    “尽管存在错误,但继续生成代码”部分专门用于减少编译失败期间报告的编译错误的数量。更少丢失的类 = 更少丢失的声明错误(希望如此)。或者,不要创建注释处理器,以煽动用户引用它们生成的代码。但是您仍然必须应对这种情况,当某些注解处理器生成代码,并使用您的注解进行注解时——与“提前”声明不同,用户会期望它是开箱即用的。

    回到最初的问题:由于 Spring 配置验证处理器不会生成任何代码(希望我没有深入研究它),但应该始终报告扫描配置中的所有错误,理想情况下它应该像这样工作:忽略 errorRaised并将配置扫描推迟到 processingOver返回 true:这将避免在多轮编译期间多次报告相同的错误,并允许注释处理器生成新的配置片段。

    可悲的是,有问题的处理器看起来已经废弃(自 2015 年以来没有提交),但作者在 Github 上很活跃,所以也许你可以向他们报告这个问题。

    同时,我建议您向经过深思熟虑的注释处理器学习,例如 Google Auto、Dagger2 或 my tiny research project .

    关于java - 注解处理,RoundEnvironment.processingOver(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47779403/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com