Quarkus Maven 依赖解析器改进

依赖解析通常是构建过程中最耗时的任务之一。考虑到 Quarkus 有其特定的 Quarkus 扩展依赖模型,它自带了自己的 Maven 依赖解析器。Quarkus 依赖解析器用于需要解析应用程序依赖的每个构建阶段,这包括构建最终包、在开发和测试模式下引导应用程序。

Quarkus 依赖解析器做什么?

从 Quarkus 依赖解析器的角度来看,Quarkus 项目 POMs 中声明的依赖是运行时依赖(除了某些 `provided` 范围的情况)。它们是运行时的,因为它们最终会出现在最终应用程序的运行时类路径上(对于 `test` 依赖,是测试运行时类路径)。这些原始项目依赖作为 Quarkus 依赖解析器的输入。

Quarkus 依赖解析器的任务是解析必要的 Quarkus 扩展构建时依赖(即带有 `-deployment` 后缀的那些及其依赖),以创建一个完整的 Quarkus 应用程序构建时依赖树,用于初始化应用程序的构建类路径。

新的孵化中 Quarkus 依赖解析器

新的(仍在孵化中的)Quarkus 依赖解析器实现背后的目标是:

  1. 改进性能,基本上更有效地解析依赖。

  2. 收集更全面的依赖信息,并将其暴露给其他与依赖分析相关的工具。

虽然此实现仍在进行中,但与当前实现相比,它已经提供了一些好处,并且可以通过设置项目属性 `quarkus.bootstrap.incubating-model-resolver` 来在 Quarkus 项目中启用,方法是在 `mvn` 命令行的末尾添加 `-Dquarkus.bootstrap.incubating-model-resolver`,或者将其添加为 `pom.xml` 中的一个属性。

当 `quarkus.bootstrap.incubating-model-resolver` 设置在其他配置源(如 `application.properties`)中时,它将不起作用,因为它需要在引导过程的早期使用,在 MP `Config` 初始化之前。

性能改进

新的依赖解析器实现虽然基于 Maven Resolver API,但尝试并行执行 Quarkus 特定的依赖分析和解析。

原始应用程序依赖仍将由 Maven 按通常方式下载,因为 Maven 构建过程很可能在 Quarkus 目标运行之前就需要它们。但是,当 Quarkus 应用程序被引导时(例如运行 `QuarkusTest`),Quarkus 依赖解析器将(重新)解析它们(通常从本地 Maven 存储库)。

即使所有工件都存在于本地 Maven 存储库中,它们仍将被解析,但会从本地 Maven 存储库而不是远程存储库解析。此处描述的性能增强适用于这两种情况:远程和本地依赖解析。

新实现中添加的增强功能在声明了多个 Quarkus 扩展依赖以及通常依赖项较多的项目中会更明显。

一种简单但不是非常精确的检查增强功能对项目影响的方法是运行以下命令:

$ mvn quarkus:dependency-tree -Dquarkus.bootstrap.incubating-model-resolver

这并不十分精确,因为除了解析 Quarkus 依赖项之外,还涉及更多与 Maven 相关的操作。因此,计算大约 10 次运行的平均时间可以捕捉差异。另一种选择是运行 `mvn test -Dtest=SoSimpleTest`。

依赖解析通常是为了准备执行其他操作而执行的,例如运行测试或启动构建。因此,在所有依赖项都本地可用时,它可能不会对给定目标的单次运行的整体时间产生巨大影响。

对于像 Quarkus CLI 这样的项目,使用孵化模型解析器(新的解析器从本地 Maven 存储库解析依赖项需要的时间减少了约 25%),`quarkus:dependency-tree` 的速度似乎快了约 100 毫秒。引导类似的应用程序进行 `QuarkusTest` 将减少约 125 毫秒的时间,但整体只快约 10%,因为从测试进程解析依赖项将无法受益于主 Maven 进程中使用的 Maven 解析器依赖项缓存(除其他外)。

在开发 UI 中显示依赖图

当在命令行或 POM 中的项目属性中启用 `quarkus.bootstrap.incubating-model-resolver` 选项时,开发 UI 将显示一个新的菜单项 - `Dependencies`。

单击 `Dependencies` 将显示应用程序的完整依赖图,包括应用程序运行时依赖(显示为绿色)、Quarkus 构建时依赖(显示为蓝色)以及应用程序的根节点(显示为红色)。

对于运行时视图,可以通过单击右上角的 `deployment` 来隐藏 Quarkus 构建时(部署)依赖项。

单击依赖项节点会将图缩小到从根到所选节点的所有可能路径。这也可以通过从下拉列表中选择一个依赖项来完成。

在 CLI 中显示全面的依赖信息

在命令行或 POM 中的项目属性中启用 `quarkus.bootstrap.incubating-model-resolver` 选项,将为 `quarkus:dependency-tree` 目标启用几个新选项:`graph` 和 `verbose`。

Graph 选项

默认情况下,`quarkus:dependency-tree` 目标显示 Quarkus 构建时依赖树。但是,树不暴露关于每个单独工件依赖的全面信息。`-Dgraph` 正是做到了这一点。

$ mvn quarkus:dependency-tree -Dquarkus.bootstrap.incubating-model-resolver -Dgraph

除了之前显示的依赖项之外,上面的命令还将显示一些以 `[+]` 结尾的依赖项,这表示该依赖项也是父节点的依赖项,但其依赖项(如果有)在图中的其他位置展开。

例如,在下面的片段中,`io.netty:netty-common` 和 `io.netty:netty-buffer` 显示了两次:

[INFO]    │     └─ io.vertx:vertx-core:4.5.7 (compile)
[INFO]    │        ├─ com.fasterxml.jackson.core:jackson-core:2.17.1 (compile) [+]
[INFO]    │        ├─ io.netty:netty-common:4.1.108.Final (compile)
[INFO]    │        ├─ io.netty:netty-buffer:4.1.108.Final (compile)
[INFO]    │        │  └─ io.netty:netty-common:4.1.108.Final (compile) [+]
[INFO]    │        ├─ io.netty:netty-transport:4.1.108.Final (compile)
[INFO]    │        │  ├─ io.netty:netty-buffer:4.1.108.Final (compile) [+]
[INFO]    │        │  ├─ io.netty:netty-common:4.1.108.Final (compile) [+]
[INFO]    │        │  └─ io.netty:netty-resolver:4.1.108.Final (compile) [+]

Verbose 选项

添加 `-Dverbose` 将为每个依赖项添加一些信息,例如它是仅运行时还是仅构建时依赖项,它是否是 Quarkus 扩展,或者在开发模式下是否是热重载依赖项。

例如,下面是运行命令的输出片段:

$ mvn quarkus:dependency-tree -Dquarkus.bootstrap.incubating-model-resolver -Dverbose
[INFO]    │  ├─ io.quarkus:quarkus-vertx-deployment:3.11.0.CR1 (compile, build-time classpath)
[INFO]    │  │  ├─ io.quarkus:quarkus-netty-deployment:3.11.0.CR1 (compile, build-time classpath)
[INFO]    │  │  │  └─ io.quarkus:quarkus-netty:3.11.0.CR1 (compile, runtime classpath, extension)
[INFO]    │  │  │     ├─ io.netty:netty-codec:4.1.108.Final (compile, runtime classpath)
[INFO]    │  │  │     └─ com.aayushatharva.brotli4j:brotli4j:1.16.0 (compile, runtime classpath)
[INFO]    │  │  │        ├─ com.aayushatharva.brotli4j:service:1.16.0 (compile, runtime classpath)
[INFO]    │  │  │        └─ com.aayushatharva.brotli4j:native-linux-x86_64:1.16.0 (compile, runtime classpath)
[INFO]    │  │  ├─ io.quarkus:quarkus-vertx:3.11.0.CR1 (compile, runtime classpath, extension)
[INFO]    │  │  │  ├─ io.netty:netty-codec-haproxy:4.1.108.Final (compile, runtime classpath)
[INFO]    │  │  │  ├─ io.quarkus:quarkus-vertx-latebound-mdc-provider:3.11.0.CR1 (compile, runtime classpath)
[INFO]    │  │  │  └─ io.smallrye:smallrye-fault-tolerance-vertx:6.3.0 (compile, runtime classpath)
[INFO]    │  │  └─ io.quarkus:quarkus-jackson-spi:3.11.0.CR1 (compile, build-time classpath)

未来增强计划

在 3.12 版本中,将有其他一些与依赖解析相关的性能改进,主要针对 `quarkus:dev` 和 `test` Maven 目标。在 3.12 版本中,孵化应用程序模型解析器将在开发模式下默认启用。一旦收集到关于其运行情况的足够反馈,孵化实现将取代当前的应用程序模型解析器实现。