1. 概述

本教程将深入探讨 Maven 中两个核心标签:dependencyManagementdependencies这两个特性在多模块项目中尤为重要

我们将分析它们的异同点,并揭示开发者在使用过程中容易踩的坑——这些坑往往会导致令人困惑的问题。

2. 基本用法

核心目标:通过 dependencyManagement 避免在 dependencies 中重复声明版本号和作用域。所有依赖项将在父 POM 文件中集中管理。

2.1. dependencyManagement 标签

本质是一个依赖声明容器,包含多个 dependency 子元素。每个依赖至少需要三个核心标签:

  • groupId
  • artifactId
  • version
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.14.0</version>
        </dependency>
    </dependencies>
</dependencyManagement>

关键点:以上代码仅声明了 commons-lang3 依赖,但不会实际添加到项目依赖列表中。

2.2. dependencies 标签

实际依赖管理容器,每个依赖至少需要:

  • groupId
  • artifactId

完整示例(包含版本号):

<dependencies>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.14.0</version>
    </dependency>
</dependencies>

当 POM 中已存在 dependencyManagement 声明时,versionscope 可被隐式继承

<dependencies>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
</dependencies>

3. 相似之处

两个标签的核心目标都是声明第三方依赖或子模块依赖,它们是互补关系。

典型工作流:

  1. 在父 POM 中定义 dependencyManagement
  2. 在子模块中使用 dependencies 引用

重要提醒dependencyManagement 中的声明只是"预告片",不会真正引入依赖。

以 JUnit 为例:

依赖声明(父 POM)

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

实际引入(子模块 POM)

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
</dependencies>

关键观察

  • 两个片段都定义了相同的 groupIdartifactId
  • 子模块中省略了 versionscope,但完全合法
  • 子模块将自动继承父 POM 中声明的版本和作用域

4. 核心差异

4.1. 结构差异

根本区别在于继承机制

  • dependencyManagement:定义依赖的元数据(版本、作用域等)
  • dependencies:实际引入依赖,可继承元数据

4.2. 行为差异

最易踩坑的区别

  • dependencyManagement纯声明,不引入任何依赖
  • dependencies实际生效,真正将依赖加入项目

例如:在 dependencyManagement 中声明 JUnit 不会使 JUnit 出现在任何类路径中,必须通过 dependencies 标签才能真正引入。

5. 实战案例

几乎所有基于 Maven 的开源项目都采用此机制。以 Maven 项目自身为例:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.hamcrest</groupId>
            <artifactId>hamcrest-core</artifactId>
            <version>2.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.hamcrest</groupId>
        <artifactId>hamcrest-core</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

6. 典型应用场景

多模块项目是最佳实践场景

想象一个包含多个模块的大型项目:

  • 每个模块独立管理依赖 → 版本混乱
  • 不同开发者使用不同版本 → 依赖冲突地狱

简单粗暴的解决方案

  1. 在根 POM(父模块)中使用 dependencyManagement 统一声明
  2. 在子模块 POM 中通过 dependencies 引用
  3. 父模块自身也可使用此机制(如需要)

单模块项目是否需要?
虽然在多模块项目中价值最大,但作为最佳实践,单模块项目也建议采用——提升可读性,并为未来扩展做准备。

7. 常见踩坑点

最典型的错误:只在 dependencyManagement 中声明依赖,忘记在 dependencies 中引用。

错误示例:

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.14.0</version>
        </dependency>
    </dependencies>
</dependencyManagement>

当在子模块中使用时:

import org.apache.commons.lang3.StringUtils;

public class Main {
    public static void main(String[] args) {
        StringUtils.isBlank(" "); // 编译报错!
    }
}

编译器直接报错:

[ERROR] Failed to execute goal compile (default-compile) on project sample-module: Compilation failure
[ERROR] ~/sample-module/src/main/java/com/baeldung/Main.java:[3,32] package org.apache.commons.lang3 does not exist

修复方案:在子模块 POM 中添加:

<dependencies>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
</dependencies>

8. 总结

我们深入剖析了 Maven 中 dependencyManagementdependencies 标签:

  • 核心差异:声明 vs 实际引入
  • 协同机制:通过继承实现依赖统一管理
  • 最佳实践:多模块项目中集中声明,子模块按需引入

完整示例代码可在 GitHub 获取。


原始标题:Maven dependencyManagement vs. dependencies Tags | Baeldung