Skip to content

传递依赖

Maven 自动包含传递依赖,从而避免了开发者手动查找并指定项目依赖所需的库。

这一特性通过读取远程仓库中的依赖项目文件来实现。通常,所有这些项目的依赖项都会被引入当前项目,包括它们继承自父级项目或其自身依赖的库,依此类推。

传递依赖的层级没有限制,唯一的问题是如果发现循环依赖,可能会导致构建失败。

由于传递依赖会迅速增加所包含的库数量,Maven 提供了一些额外的机制来限制包含的依赖项。

  • 依赖调解 - 当遇到多个版本的相同构件作为依赖时,依赖调解机制决定使用哪个版本。Maven 选择“最近定义”的版本,即在依赖树中距离项目最近的版本。如果需要确保使用特定版本,可以在项目的 POM 文件中显式声明该版本。需要注意的是,如果两个依赖版本在依赖树中的深度相同,则先声明的版本会被采用。

    • "最近定义"意味着使用的版本将是依赖树中离项目最近的版本。考虑以下依赖树:

        A
        ├── B
        │   └── C
        │       └── D 2.0
        └── E
            └── D 1.0
    • 在文本中,A、B 和 C 的依赖关系定义为 A -> B -> C -> D 2.0 和 A -> E -> D 1.0,那么在构建 A 时,将使用 D 1.0,因为通过 E 到 D 的路径更短。你可以在 A 中显式添加对 D 2.0 的依赖,以强制使用 D 2.0,如下所示:

        A
        ├── B
        │   └── C
        │       └── D 2.0
        ├── E
        │   └── D 1.0
      
        └── D 2.0
  • 依赖管理 - 这允许项目作者直接指定在遇到传递依赖或未指定版本的依赖时要使用的构件版本。在前一节的示例中,即使 A 没有直接使用 D,A 也可以在其 dependencyManagement 部分包含 D 作为依赖,并直接控制在何时或是否引用时使用哪个版本的 D。

  • 依赖范围 - 这允许你仅包含当前构建阶段所需的依赖。下面会详细描述。

  • 排除依赖 - 如果项目 X 依赖于项目 Y,且项目 Y 依赖于项目 Z,项目 X 的所有者可以使用 "exclusion" 元素显式排除项目 Z 作为依赖。

  • 可选依赖 - 如果项目 Y 依赖于项目 Z,项目 Y 的所有者可以使用 "optional" 元素将项目 Z 标记为可选依赖。当项目 X 依赖于项目 Y 时,X 只会依赖于 Y,而不会依赖于 Y 的可选依赖 Z。项目 X 的所有者可以选择显式添加对 Z 的依赖。(可以把可选依赖理解为“默认排除”。)

尽管传递依赖可以隐式地包括所需的依赖,但显式指定源代码直接使用的依赖是一个良好的实践。尤其是当项目的依赖发生变化时,这一最佳实践的价值尤为明显。

例如,假设你的项目 A 指定了对项目 B 的依赖,而项目 B 又指定了对项目 C 的依赖。如果你直接使用了项目 C 中的组件,但没有在项目 A 中指定项目 C,那么当项目 B 突然更新/移除其对项目 C 的依赖时,可能会导致构建失败。

另一个直接指定依赖的原因是,它为你的项目提供了更好的文档:人们可以通过阅读项目中的 POM 文件或执行 mvn dependency:tree 来获取更多信息。

Maven 还提供了 dependency:analyze 插件目标,用于分析依赖:它有助于实现这一最佳实践。