Gradle 中的依赖项解析包含两个主要步骤

  1. 图解析

  2. 构件解析

dep man basics 1

1. 图解析

图解析 是确定给定的一组声明的依赖项所需的完整传递依赖项集及其版本的过程。

图解析仅在依赖项元数据(GMM、POM)上操作。在此阶段,构件(JAR)不会被解析。此时仅计算图的结构,基于依赖项之间的关系。

1. 发现依赖项

dep man basics 2

图解析从构建脚本中声明的项目和外部(模块)依赖项开始。

一个项目向依赖项图贡献一个组件,该组件本身属于一个模块。

在下面的示例中,组件 com.fasterxml.jackson.core:jackson-databind:2.17.2 作为依赖项添加到 Java 应用程序中的 implementation 配置

build.gradle.kts
dependencies {
    implementation("com.fasterxml.jackson.core:jackson-databind:2.17.2")
}

2. 执行冲突解决

dep man basics 3

当多个声明或传递依赖项请求同一模块的不同版本时,Gradle 会识别并解决任何版本冲突。

即使用户可能声明模块的 2.17.2 版本,这可能不是最终在图中解析的版本。Gradle 的冲突解决策略(默认为选择最高版本)在请求多个版本时选择模块的单个版本。

但是,可以使用 Gradle API 来更改结果

  • 解析规则:Gradle 允许配置规则以强制执行特定版本、拒绝某些版本或根据需要替换依赖项。

  • 依赖项替换:在构建逻辑中定义的规则可以用另一个依赖项替换一个依赖项,更改版本,或将对一个模块的请求重定向到另一个模块。

  • 动态版本:如果依赖项使用动态版本(例如,1.0.+)或版本范围(例如,[1.0, 2.0))定义,Gradle 会通过查询仓库将这些解析为特定版本。

  • 依赖项锁定:如果启用,Gradle 会检查锁定文件以确保跨构建调用的版本一致,从而防止依赖项版本发生意外更改。

在该示例中,Gradle 选择组件 com.fasterxml.jackson.core:jackson-databind:2.17.2com.fasterxml.jackson.core:jackson-databind 模块2.17.2 版本)。

3. 检索元数据

dep man basics 4

一旦 Gradle 确定要解析的外部模块的版本,它就会从仓库中的 ivypomGMM 元数据文件中获取组件元数据

这是一个 com.fasterxml.jackson.core:jackson-databind:2.17.2元数据示例

{
  "formatVersion": "1.1",
  "component": {
    "group": "com.fasterxml.jackson.core",
    "module": "jackson-databind",
    "version": "2.17.2",
  },
  "variants": [
    {
      "name": "apiElements"
    },
    {
      "name": "runtimeElements",
      "attributes": {
        "org.gradle.category": "library",
        "org.gradle.dependency.bundling": "external",
        "org.gradle.libraryelements": "jar",
        "org.gradle.usage": "java-runtime"
      },
      "dependencies": [
        {
          "group": "com.fasterxml.jackson.core",
          "module": "jackson-annotations",
          "version": {
            "requires": "2.17.2"
          }
        },
        {
          "group": "com.fasterxml.jackson.core",
          "module": "jackson-core",
          "version": {
            "requires": "2.17.2"
          }
        },
        {
          "group": "com.fasterxml.jackson",
          "module": "jackson-bom",
          "version": {
            "requires": "2.17.2"
          }
        }
      ],
      "files": [
        {
          "name": "jackson-databind-2.17.2.jar"
        }
      ]
    }
  ]
}

如您所见,com.fasterxml.jackson.core:jackson-databind:2.17.2 组件提供两个变体

  • apiElements 变体包含针对 Jackson Databind 编译项目所需的依赖项。

  • runtimeElements 变体包含在运行时执行 Jackson Databind 所需的依赖项。

变体是为特定用例或环境量身定制的组件的特定变体。变体允许您根据组件的使用上下文提供不同的组件定义。

每个变体都包含一组构件并定义一组依赖项runtimeElements 变体提供 jackson-databind-2.17.2.jar 构件,该构件将在构件解析阶段稍后下载。

4. 更新图

dep man basics 5

Gradle 构建一个依赖项图,表示配置的依赖项及其关系。此图包括直接依赖项(在构建脚本中显式声明)和传递依赖项(直接依赖项和其他传递依赖项的依赖项)。

依赖项图由节点组成,其中

  • 每个节点代表一个 变体

  • 每个 依赖项组件 中选择一个 变体

这些节点通过边连接,表示 变体 之间的依赖关系。边指示一个变体如何依赖于另一个变体。

例如,如果您的项目依赖于 Jackson Databind,而 Jackson Databind 依赖于 jackson-annotations,则图中的边表示 jackson-annotations 是 Jackson Databind 的一个变体的依赖项。

可以使用 dependencies task 可视化依赖项图的结构

$ ./gradlew app:dependencies

[...]

runtimeClasspath - Runtime classpath of source set 'main'.
\--- com.fasterxml.jackson.core:jackson-databind:2.17.2
     +--- com.fasterxml.jackson.core:jackson-annotations:2.17.2
     |    \--- com.fasterxml.jackson:jackson-bom:2.17.2
     |         +--- com.fasterxml.jackson.core:jackson-annotations:2.17.2 (c)
     |         +--- com.fasterxml.jackson.core:jackson-core:2.17.2 (c)
     |         \--- com.fasterxml.jackson.core:jackson-databind:2.17.2 (c)
     +--- com.fasterxml.jackson.core:jackson-core:2.17.2
     |    \--- com.fasterxml.jackson:jackson-bom:2.17.2 (*)
     \--- com.fasterxml.jackson:jackson-bom:2.17.2 (*)

在此输出中,runtimeClasspath 表示项目中的特定可解析配置。每个可解析配置都计算一个单独的依赖项图。

对于同一组声明的依赖项,不同的配置可以解析为不同的传递依赖项集。每个 变体 都由 组件 的特定版本拥有。

5. 选择变体

dep man basics 6

根据构建的需求,Gradle 选择模块的 变体 之一。

为了描述和区分 变体,您使用 属性属性用于定义 变体 的特定特征或属性以及应使用这些 变体 的上下文。

在 Jackson Databind 的元数据中,我们看到 runtimeElements 变体由 org.gradle.categoryorg.gradle.dependency.bundlingorg.gradle.libraryelementorg.gradle.usage 属性描述

{
  "variants": [
    {
      "name": "runtimeElements",
      "attributes": {
        "org.gradle.category": "library",
        "org.gradle.dependency.bundling": "external",
        "org.gradle.libraryelements": "jar",
        "org.gradle.usage": "java-runtime"  (1)
      }
    }
  ]
}
1 对于 apiElements 变体,此属性不同:“org.gradle.usage": "java-api"`

属性用于在依赖项解析期间选择适当的 变体

在我们的 Java 应用程序示例中,其中 Jackson Databind 作为依赖项,Gradle 将选择 runtime 变体来构建应用程序。

要查看 Gradle 为给定配置解析了哪个 变体 的更详细视图,您可以运行 dependencyInsight task

$ ./gradlew :app:dependencyInsight --configuration runtimeClasspath --dependency com.fasterxml.jackson.core:jackson-databind:2.17.2

> Task :app:dependencyInsight

com.fasterxml.jackson.core:jackson-databind:2.17.2 (by constraint)
 Variant runtimeElements:
    | Attribute Name                 | Provided     | Requested    |
    |--------------------------------|--------------|--------------|
    | org.gradle.status              | release      |              |
    | org.gradle.category            | library      | library      |
    | org.gradle.dependency.bundling | external     | external     |
    | org.gradle.libraryelements     | jar          | jar          |
    | org.gradle.usage               | java-runtime | java-runtime |
    | org.gradle.jvm.environment     |              | standard-jvm |
    | org.gradle.jvm.version         |              | 11           |

com.fasterxml.jackson.core:jackson-databind:2.17.2
+--- runtimeClasspath
\--- com.fasterxml.jackson:jackson-bom:2.17.2
     +--- com.fasterxml.jackson.core:jackson-annotations:2.17.2
     |    +--- com.fasterxml.jackson:jackson-bom:2.17.2 (*)
     |    \--- com.fasterxml.jackson.core:jackson-databind:2.17.2 (*)
     +--- com.fasterxml.jackson.core:jackson-core:2.17.2
     |    +--- com.fasterxml.jackson:jackson-bom:2.17.2 (*)
     |    \--- com.fasterxml.jackson.core:jackson-databind:2.17.2 (*)
     \--- com.fasterxml.jackson.core:jackson-databind:2.17.2 (*)

在此示例中,Gradle 将 jackson-databindruntimeElements 变体 用于 runtimeClasspath 配置。

2. 构件解析

构件解析在依赖项图构建完成后发生。对于依赖项图中的每个节点,Gradle 获取必要的物理文件(构件)。

此过程使用已解析的图和仓库定义来生成所需的输出文件。

1. 获取构件

dep man basics 7

Gradle 定位并下载图中引用的实际 构件(例如 JAR 文件、ZIP 文件等)。这些构件对应于在图解析期间发现的节点。

在我们的示例中,Gradle 在依赖项图解析期间解析了 com.fasterxml.jackson.core:jackson-databindruntimeElements 变体。该 变体 对应于 JAR 文件 jackson-databind-2.17.2.jar 作为构件

{
  "component": {
    "group": "com.fasterxml.jackson.core",
    "module": "jackson-databind",
    "version": "2.17.2"
  },
  "variants": [
    {
      "name": "apiElements",
      "dependencies": [],
      "files": [
        {
          "name": "jackson-databind-2.17.2.jar"
        }
      ]
    }
  ]
}

Gradle 还获取 Jackson Databind 的已解析的传递依赖项,包括 jackson-annotationsjackson-core,它们分别对应于 jackson-annotations-2.17.2.jarjackson-core-2.17.2.jar

2. 转换构件

dep man basics 8

如果需要或请求,Gradle 可以使用 构件转换 来转换构件。当 Gradle 需要将一种构件格式转换为构建所需的另一种构件格式时,转换通常在依赖项解析期间自动应用。

例如,jackson-databind 可能只生成一个名为 jackson-databind-2.17.2.zip 的 ZIP 文件作为构件,但构建需要 jackson-databind-2.17.2.jar。Gradle 可以使用 Gradle 提供的转换或用户编程的转换将 zip 文件转换为 jar 文件。