【问题标题】:Java, Is index annotated classes at compile time with Annotation Processor a good practice?Java,在编译时使用注释处理器索引注释类是一个好习惯吗?
【发布时间】:2015-03-23 15:19:36
【问题描述】:

我正在考虑在编译时使用注解处理器为使用某种注解类型注解的类构建索引(甚至是存储在文件中的简单列表),以便在运行时加快注解类检索。

那么,这是一个好习惯吗?有什么缺点吗?如果它和我现在认为的一样好,为什么没有很多库可以轻松地做到这一点(我发现的唯一一个是Class Index)?而不是运行时处理有这么多?

【问题讨论】:

  • 这听起来像premature optimization 对我来说。您是否执行过任何基准测试?
  • ClassIndex 做了一些有趣的基准测试,我自己没有做过。这可能是过早的优化,但问题仍然存在。我认为这也是我的图书馆的一个重要设计问题。
  • 从您的链接中引用 大大加快 Java 应用程序引导,您(通常)会说多少次(通常)您的(或您的用户的)应用程序( s)通常在单次运行期间引导?最后,“Reflections Maven 插件”似乎表现得几乎一样好(在该单一发布的基准上)......
  • 可能运行时处理更常见,因为它更有用且更强大。编译时处理仅适用于发现编译时可用的类(或其他)。想象一下,您正在使用注释使您的库可插入,或者因为您的库提供了最终用户应该在其代码中应用的注释(例如,Jackson)。编译时注释处理不是一个选项,因为在编译 your 库时代码中不存在此类注释。
  • @MattBall,如果客户端代码在META-INF/services/javax.annotation.processing.Processor 中,我的处理器不是在编译时运行吗??

标签: java annotations annotation-processing


【解决方案1】:

JBoss WildFly 的一个子项目,你可能会感兴趣:Jandex.

它在构建时(并且索引文件可以添加到 JAR)或运行时(通过检查类文件而不是通过反射来检索注释)创建注释索引,显着提高了注释检索的性能,因为它避免了实际需要加载类。

Jandex 听起来很像您所追求的。

【讨论】:

    【解决方案2】:

    作为 ClassIndex 库的作者,我可以列出使用注释处理进行注释索引的几个优点,但也有一个缺点,我认为它阻碍了它的广泛采用。

    优点:

    • 使用注释处理的索引基于官方 JSR 269。另一方面,类路径扫描依赖于 Java 内部。众所周知,ClassLoader 没有用于检索带注释的类列表的 API。但更令人惊讶的是,通用 ClassLoader 不允许列出它尝试加载类的文件夹和 JAR 文件。类路径扫描器假定用于加载类的唯一类加载器是 URLClassLoader,它允许检索源 URL 以使用 getURLs() method 进行扫描。
    • 在某些环境中,通常使用类路径扫描器 do not work,这就是在 Android 上使用 Dalvik Executable 格式的情况。
    • 恒定的运行时复杂性使编译时索引速度超快。
    • Project Jigsaw 计划带来annotation detection to Java。当前的要求还建议将编译时索引作为可行的实现。它甚至引入了@Indexed 元注释,其目的与 ClassIndex 库中的@IndexAnnotated 相同。

    缺点:

    • Java 编译器和工具对 JSR 269 的支持很差。早期的 javac 版本中有几个错误。 Eclipse 的自定义 JDT 编译器有更多的错误,并且它不支持注释处理器的自动发现。 ClassIndex 包含许多此类问题的解决方法。
    • 类路径扫描是事实标准,得到很好的支持。
    • 应用程序启动时间很少是需要担心的瓶颈。

    【讨论】:

      【解决方案3】:

      我认为主要的缺点是它更复杂。注释处理是一个全新的 API 和概念,许多开发人员并不熟悉。反射 API 更简单,也更广为人知。您通常可以在运行时完成相同的任务。

      如果更好的启动性能至关重要(这种情况很少发生),那么增加复杂性也许是值得的。

      不过,我不相信基准测试。他们声明“类路径大小设置为 121MB”——一个任意值,使得与硬编码或编译时处理的任何比较完全无用。你为什么要扫描整个类路径呢?在大多数情况下,仅扫描开发人员类会更合理。

      许多框架使用配置文件或具有 API 来限制需要扫描的类或包。这会显着增加启动时间。

      为什么没有很多图书馆可以做到这一点

      许多 OSGi 工具/框架都可以做到这一点。在编译时扫描注释并将元数据写入 jar 清单文件,或者它们创建更复杂的元数据文件。我怀疑这样做的主要原因是为了保持与 bnd 和类似工具的兼容性,在注释或注释处理变得更流行之前,这些工具已被用于构建和编译 OSGi 组件的时间分析。此外,OSGi 组件有自己的生命周期,可以随时来来去去。因此,在这种情况下,启动时间确实更重要,因为您不能只在应用程序启动时扫描一次。每当组件(重新)启动时,您都需要扫描注释。

      我不会说这是一个好的或坏的做法。在适合您的需要时使用此技术。为了几毫秒的启动时间,我会避免增加太多的复杂性。

      【讨论】:

      • 感谢您的显着回答,对获得更广泛的视野非常有帮助。考虑到这一点,我实际上发现了编译时索引的一个缺点,即缺乏对可插入库的适用性,例如抽象工厂和 SPI 的实现。我不确定我是否应该把它放在我自己问题的答案中。
      猜你喜欢
      • 1970-01-01
      • 2017-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-16
      • 1970-01-01
      • 2021-01-10
      相关资源
      最近更新 更多