当前位置: 首页 > 面试题库 >

注释处理,RoundEnvironment.processingOver()

艾望
2023-03-14
问题内容

在阅读 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将尝试运行这些处理器并重试编译,以期它们生成丢失的代码。这称为“多轮编译”。

编译顺序如下所示:

  1. 初级轮次(可能与代码生成有关);
  2. 几轮可选的代码生成;新的回合将发生,直到注释处理器什么都不会产生为止;
  3. 最后一轮;在此回合期间生成的代码将不会进行注释处理。

每一轮都是对代码进行全面的尝试。除最后一轮外,每一轮都将重新运行代码上的每个注释处理器,这些代码先前是由注释处理器生成的。

这个美妙的序列允许使用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

  1. 如果您的处理器生成了新的公开可见的类(可以如上所述在用户代码“提前”中使用),则您必须超级灵活:继续生成,在可能的情况下忽略丢失的位和不一致之处,并 忽略 errorRaised。这样可以确保在Javac继续进行错误报告时,您会尽可能少地丢失一些东西。
  2. 如果您的代码未生成新的公开可见的类(例如,因为它仅创建程序包私有的类,而其他代码将在运行时反射性地查找它们,请参见ButterKnife),那么您应errorRaised尽快检查并在退出时立即退出返回true。这将简化您的代码并加快错误编译的速度。

processingOver

  1. 如果当前回合不是最后一回(processingOver返回false),则尝试生成尽可能多的输出;否则,返回0。忽略用户代码中缺失的类型和方法(假设其他注释处理器可能在随后的回合中生成它们)。但是在其他注解处理器可能需要的情况下,仍要尝试尽可能多地生成。例如,如果在每个类上触发了用注释的代码生成@Entity,则应迭代这些类并尝试为每个类生成代码,即使先前的类有错误或缺少方法也是如此。就个人而言,我只是将每个单独的生成单元都包装在try-catch中,并检查processingOver:如果为假,请忽略错误并继续遍历注释并生成代码。这样,Javac可以通过运行它们直到完全满足为止,从而打破由不同注释处理器生成的代码之间的循环依赖关系。
  2. 如果当前回合不是最后一回(processingOver返回false),并且未处理某些先前回合的注释(由于异常导致处理失败,我记得它们),请重试这些注释。
  3. 如果当前回合是最后一回(processingOver返回true),请检查是否还有未处理的注释。如果是这样,则编译失败(仅在 一轮!)

上面的序列是 预期的 使用方式processingOver

一些注释处理器的用法processingOver有些不同:它们缓冲在每一轮中生成的代码,并Filer在最后一轮中实际将其写入。这样可以解决对其他处理器的依赖关系,但可以防止
其他 处理器查找“小心”处理器生成的代码。这是一个讨厌的策略,但是如果生成的代码不打算在其他地方引用,我想它还可以。

还有注释处理器,例如上述的第三方Spring配置验证器:它们误解了某些东西,并以猴子和扳手的方式使用API​​。

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

“尽管存在错误仍保留生成代码”部分专门用于减少编译失败期间报告的编译错误数。更少的类丢失=希望的声明错误更少。或者,不要创建注释处理器,它会诱使用户引用由它们生成的代码。但是,您仍然必须应对这种情况,当某些注释处理器生成的代码带有注释时,与“提前”声明不同,用户期望开箱即用。

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

令人遗憾的是,所讨论的处理器看起来已经废弃了(自2015年以来没有提交),但是作者活跃在Github上,因此也许您可以向他们报告该问题。

同时,我建议您向经过深思熟虑的注释处理器学习,例如Google
Auto,Dagger2或我的小型研究项目。



 类似资料:
  • 在阅读Java中的自定义注释处理器的代码时,我注意到处理器的方法中的这段代码: 碰巧我也在处理一个自定义注释处理器&我想在我的注释处理器中使用上面的代码片段。 但是我没有注意到处理器的行为有任何变化。我得到了检查,但我看不出有什么用。 我想知道在处理某个回合时使用有用的用例。

  • 主要内容:使用Rem语句注释,注释使用::声明为创建的脚本添加注释或文档总是一个好习惯。 这是一个维护脚本用来理解脚本实际所做的事情所必需的注释。 例如,考虑下面这段没有注释形式的代码。 如果一个没有任何注释的脚本,普通人试图理解脚本,那么需要很多时间来理解脚本做些什么工作。 使用Rem语句注释 有两种方法可以在批处理脚本中创建注释; 一个是通过命令。 语句后的任何文本都将被视为注释,不会被执行。 以下是此声明的一般语法。 语法 其中是需要添

  • 需要注释处理器的帮助。我创建了一个简单的注释处理器,它使用@autoservice注释来检查注释的字段是否为最终字段。但它没有显示任何编译时错误。这是我的配置 注释: 注释处理器: pom文件: 测试文件:

  • 我正在使用注释处理器来处理方法参数的注释。 用于参数的注释类型有一个注释@参数 现在,当注释处理器运行时,我想检查参数注释()是否有参数注释。我通过执行以下代码来实现这一点。 由于某种原因,arg始终为空。是否有注释未返回的原因?

  • 我在编译使用我的注释处理器的代码时收到以下错误: 关于如何调试这个有什么提示吗?错误输出根本没有用处。有没有办法得到更详细的错误?

  • 问题内容: 我有一个名为@Pojo的自定义注释,可用于自动生成Wiki文档: 我这样使用它: 注释资源方法,以便注释处理器可以自动生成描述所需资源和类型的Wiki页面。 我需要在注释处理器中读取该字段的值,但出现运行时错误。 在我的处理器的源代码中,我有以下几行: 但实际的类不适用于处理器。我认为我需要一个替代品来代替实际课程。我不确定该怎么买。 我得到的错误是: 该是我的一个提到的一类注释。 不