乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 介绍与核心概念

    • Maven是什么
    • 约定优于配置
  • 第2章 安装与配置

    • 安装与验证
    • settings.xml
    • 本地仓库与镜像
  • 第3章 POM与项目坐标

    • POM
    • GAV坐标
    • packaging
  • 第4章 标准目录布局

    • 标准目录布局
  • 第5章 依赖机制

    • dependencies
    • scope
    • 依赖传递
    • 依赖冲突与调解
    • exclusions
    • optional
    • dependencyManagement
  • 第6章 仓库

    • 仓库体系
    • 本地仓库
    • 远程仓库与镜像
    • 私服
  • 第7章 构建生命周期

    • 生命周期概述
    • clean 生命周期
    • default 生命周期
    • site 生命周期
    • 生命周期与插件绑定
  • 第8章 插件

    • 插件概述
    • maven-compiler-plugin
    • maven-surefire-plugin
    • maven-war-plugin
  • 第9章 继承与聚合

    • parent继承
    • 聚合
    • BOM
    • properties
  • 第10章 属性与资源过滤

    • 资源过滤
    • Profile
  • 第11章 常用命令

    • mvn compile
    • mvn test
    • mvn package
    • mvn clean
    • mvn install
    • mvn dependency:tree
  • 第12章 常见问题与最佳实践

    • 依赖冲突排查
    • 最佳实践

dependencies

本章是"依赖机制"篇章的起点。理解 dependencies 标签的作用,是掌握 Maven 声明式依赖管理的基础,也是告别"手动下载 JAR"时代的分水岭。


核心机制

Maven 通过项目对象模型(POM)中的 dependencies 部分,提供了一种声明式的依赖管理机制。开发者只需声明"项目需要什么",Maven 负责解决"从哪里获取、如何传递、版本是否冲突"等所有细节。

这句话的关键词是声明式(Declarative)。你不再是一个搬运工(手动下载 JAR),而是一个指挥官(在 pom.xml 里写下坐标,Maven 自动调度)。

什么是声明式依赖管理?

在 Maven 之前,Java 项目的依赖管理是"命令式"的——你亲自执行每一步:

  1. 打开浏览器,访问 Spring 官网
  2. 找到 spring-context-5.3.21.jar,点击下载
  3. 复制到项目的 lib/ 目录
  4. 如果 spring-context 依赖了 spring-core、spring-beans,重复步骤 1-3
  5. 手动编写 classpath:javac -cp lib/spring-context.jar:lib/spring-core.jar:lib/spring-beans.jar ...

Maven 将这一切压缩为一句声明:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.21</version>
</dependency>

dependencies 在 POM 中的位置

dependencies 是 pom.xml 中 <project> 的直接子元素,位于 <dependencies> 标签内:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- 项目坐标 -->
    <groupId>com.feixiang</groupId>
    <artifactId>employee-system</artifactId>
    <version>1.0.0</version>

    <!-- 依赖声明区 -->
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.21</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.8.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

每个 <dependency> 内部的核心子元素:

元素含义是否必填
groupId组织/公司域名倒写是
artifactId项目/模块名称是
version版本号是(子模块可从父 POM 继承)
scope依赖范围(compile/test/provided/runtime/system)否,默认 compile
optional是否可选依赖否,默认 false
exclusions排除特定传递依赖否
type依赖类型(jar/pom/war 等)否,默认 jar
classifier分类器(如 sources、javadoc)否

依赖声明前后对比

维度手动管理(无 Maven)声明式管理(Maven)
获取依赖手动下载,复制到 lib/声明坐标,mvn 自动从仓库下载
传递依赖手动追踪,漏一个就编译失败自动解析传递依赖
版本控制文件名里写版本号,容易混乱坐标里精确声明版本
团队协作新成员需要手动配齐所有 JAR执行 mvn compile 自动补齐
classpath手动拼写,容易遗漏自动计算,零配置
升级依赖删除旧 JAR,下载新 JAR,改 classpath改 version 数字,执行 mvn

生活类比:从"自己抓药"到"医院处方"

想象你生病了需要吃药:

  • 手动管理(无 Maven):你自己去药店,根据症状挑选药品。感冒需要感冒药、退烧药、维生素 C,每种药你都要亲自找、亲自核对生产日期。如果感冒药里已经包含了退烧成分,你可能重复购买,吃错剂量。
  • 声明式管理(Maven):你去医院,医生开一张处方单(pom.xml),上面写着"感冒药一盒"。药房(Maven 仓库)自动配齐所有成分——主药、辅料、说明书,并且检查药物之间是否冲突(依赖调解)。你只需把处方交给药房,拿到的是一份安全、完整的药包。

图示

上图展示了 dependencies 的核心价值:开发者从"执行者"降级为"声明者",Maven 从"工具"升级为"管家"。你只需在处方单上写药名,药房负责抓药、配药、核对禁忌。


完整示例

场景

飞翔科技的员工管理系统 employee-system 需要引入 Spring 框架和 MySQL 驱动。CTO 大翔要求所有依赖必须通过 Maven 管理,禁止手动下载 JAR。后端小崔负责引入依赖,运维李眉负责验证构建环境的一致性。

操作前:手动管理的噩梦

小崔回忆上一个非 Maven 项目的经历:

employee-system-legacy/
├── src/
│   └── com/feixiang/...
├── lib/                              # 手动维护的 JAR 仓库
│   ├── spring-context-5.3.21.jar     # 小崔从 Spring 官网下载
│   ├── spring-core-5.3.21.jar        # 漏传这个,编译报错
│   ├── spring-beans-5.3.21.jar       # 漏传这个,运行时报 NoClassDefFoundError
│   ├── spring-aop-5.3.21.jar         # 白歌提醒后才补上
│   ├── mybatis-3.5.9.jar
│   ├── mysql-connector-java-8.0.30.jar
│   └── junit-5.8.2.jar               # 版本写成了 5.8.1,测试跑不过
├── build.sh                          # 手动拼 classpath 的脚本
└── README.md                         # "请确保 lib/ 目录完整"

build.sh 片段:

CLASSPATH="lib/spring-context-5.3.21.jar:lib/spring-core-5.3.21.jar:lib/spring-beans-5.3.21.jar:lib/spring-aop-5.3.21.jar:lib/mybatis-3.5.9.jar:lib/mysql-connector-java-8.0.30.jar"
javac -cp "$CLASSPATH" src/com/feixiang/*.java

问题:

  • 小崔的 Mac 上 classpath 用 : 分隔,黄俪的 Windows 上要用 ;,脚本不通用
  • 李眉部署到 Linux 服务器时,发现 lib/ 里少了 spring-expression.jar,启动失败
  • 升级 Spring 到 5.3.22 时,小崔要手动替换 4 个 JAR 文件,漏了一个导致版本不一致

操作后:声明式依赖管理

小崔创建 Maven 项目,在 pom.xml 中声明依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.feixiang</groupId>
    <artifactId>employee-system</artifactId>
    <version>1.0.0</version>

    <dependencies>
        <!-- Spring Context:自动传递 spring-core、spring-beans 等 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.21</version>
        </dependency>

        <!-- MyBatis:持久层框架 -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>

        <!-- MySQL 驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

        <!-- JUnit:仅测试使用 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.8.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

执行命令:

mvn compile

输出:

[INFO] Scanning for projects...
[INFO] --- maven-dependency-plugin:2.8:tree ---
[INFO] com.feixiang:employee-system:jar:1.0.0
[INFO] +- org.springframework:spring-context:jar:5.3.21:compile
[INFO] |  +- org.springframework:spring-aop:jar:5.3.21:compile
[INFO] |  +- org.springframework:spring-beans:jar:5.3.21:compile
[INFO] |  +- org.springframework:spring-core:jar:5.3.21:compile
[INFO] |  |  \- org.springframework:spring-jcl:jar:5.3.21:compile
[INFO] |  \- org.springframework:spring-expression:jar:5.3.21:compile
[INFO] +- org.mybatis:mybatis:jar:3.5.9:compile
[INFO] +- mysql:mysql-connector-java:jar:8.0.30:compile
[INFO] \- org.junit.jupiter:junit-jupiter:jar:5.8.2:test
[INFO]    \- ...
[INFO] BUILD SUCCESS

变化分析:

  • 小崔只声明了 4 个依赖,Maven 自动解析出 10+ 个传递依赖(spring-aop、spring-beans、spring-core 等)
  • 黄俪在 Windows 上执行同样的 mvn compile,下载的 JAR 和小崔的 Mac 完全一致
  • 李眉部署时,只需执行 mvn package,无需关心 classpath 怎么拼
  • 升级 Spring 时,小崔只需改 version 标签里的数字,Maven 自动替换整个依赖树

易错点与常见问题

误区一:dependencies 里写得越多越好

错误认知:"我把可能用到的库都写进 dependencies,省得以后再加。"

纠正:每个 <dependency> 都会进入依赖树,增加构建时间、打包体积和潜在冲突。只声明项目直接需要的依赖。例如:你的代码里直接 import org.springframework.context.ApplicationContext,才需要声明 spring-context;spring-core 是传递依赖,不需要手动声明。

误区二:version 可以随便写,Maven 会找最近的

错误认知:"我写 version>1.0</version>,Maven 会自动找最新版。"

纠正:Maven 对版本号是精确匹配的。写 1.0 就找 1.0,不会自动升级成 1.1。如果你想使用版本范围,需要显式写 [1.0,2.0) 或 LATEST(不推荐用于生产)。生产项目应该精确锁定版本号,确保构建可复现。

误区三:dependencies 和 dependencyManagement 是一回事

错误认知:"我在父 POM 的 dependencyManagement 里写了依赖,子模块就能用了。"

纠正:dependencyManagement 只声明版本,不引入依赖。子模块需要在 dependencies 里再次声明(可以省略 version),依赖才真正进入 classpath。这是本章与后续 dependencyManagement 章节的关键区别,后续会详细展开。


小结

dependencies 标签是 Maven 声明式依赖管理的入口。开发者只需用 GAV 坐标(groupId + artifactId + version)声明项目直接需要的库,Maven 自动完成下载、传递、classpath 计算等所有细节。与手动下载 JAR 相比,声明式管理带来了版本精确控制、团队协作零摩擦、构建环境可复现的三重收益。

本章与全局的关系:本章回答了"如何声明依赖"。下一章"依赖传递"将深入讲解"你声明了 A,Maven 为什么自动引入了 B 和 C"——这是 Maven 依赖管理最精妙的设计之一。

下一页
scope