1. 概述
maven 是一个常用的 java 跨平台项目管理工具,主要工作是项目构建、依赖管理和项目信息管理。 除了编写源代码,我们每天有相当一部分时间花在了编译、运行单元测试、生成文档、打包和部署等繁琐而不起眼的工作上,这就是项目的构建过程。 而 maven 可以自动地从清理、编译、测试到生成报告、打包、部署,极大的简化了我们日常的工作,同时它还是一个依赖管理工具和项目信息管理工具,提供了中央仓库,能帮助我们自动下载构件。 作为 java 程序员必备的工具,深入研究和了解 maven 的使用是非常重要的,尤其是在构建失败、包冲突解决时,只有了解了我们即将介绍的 maven 工具的用法和要点才不会一筹莫展。 本文我们就来重点介绍一下 maven 作为依赖管理和项目信息管理工具的使用。
2. maven 依赖的声明
maven 最强大的功能就是项目中依赖的管理。 根元素 project 下的 dependencies 可以包含一个或多个 dependency 元素,以声明一个或者多个项目依赖。
代码语言:javascript复制<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
如上所示,maven 依赖由以下几个元素构成:
元素 | 是否必须 | 备注 |
---|---|---|
groupId、artifactId、version | 是 | maven 依赖的基本坐标 |
tpye | 否 | 依赖的类型,默认为 jar |
scope | 否 | 依赖的范围 |
optional | 否 | 标记依赖是否可选 |
exclusions | 否 | 排除传递性依赖选项 |
3. scope — 依赖范围
maven 在不同的阶段,使用的包加载路径是不同的。
- 编译项目主代码时使用 compile classpath
- 编译和执行测试时使用 test classpath
- 在实际运行Maven项目时使用 runtime classpath
依赖范围就是用来控制依赖于这三种 classpath 的关系的,maven 有以下几种依赖范围可供选择:
- compile — 编译依赖范围,如果没有指定,默认使用该依赖范围
- test — 测试依赖范围,使用此依赖范围的 maven 依赖,只对 test classpath 有效
- provided — 已提供依赖范围,使用此依赖范围的 maven 依赖,对于 compile 和 test classpath 有效,但在运行时无效
- runtime — 运行时依赖范围,使用此依赖范围的 maven 依赖,对于 test 和 runtime classpath 有效,但在编译主代码时无效
- system — 系统依赖范围,其与三种 classpath 的关系,和 provided 依赖范围一致
依赖范围Scope | 对于compile classpath有效 | 对于test classpath有效 | 对于runtime classpath有效 | 例子 |
---|---|---|---|---|
compile | Y | Y | Y | spring-core |
test | | Y | | JUnit |
provided | Y | Y | | servlet-api |
runtime | | Y | Y | JDBC 驱动实现 |
system | Y | Y | | 本地的 maven 仓库之类的类库文件 |
3.1. 依赖范围的传递性
如果 A 依赖 B,B 依赖 C,那么 C 对于 A 就是传递性依赖。 下表列出了依赖范围的传递性:
第一依赖 | |||||
---|---|---|---|---|---|
compile | test | provided | runtime | ||
第二依赖 | compile | compile | / | / | runtime |
test | test | / | / | test | |
provided | provided | / | provided | provided | |
runtime | runtime | / | / | runtime |
4. 依赖仲裁
maven 依赖仲裁(Dependency Mediation)遵循以下两个原则: 1. 最短路径优先原则 2. 第一声明优先原则,在依赖路径长度相等的前提下,在POM中依赖声明最靠前的依赖优先
例如,项目A有这样的依赖关系: 1. A -> B -> C -> X(1.0),A -> D -> X(2.0),X(1.0) 路径长度为 3,X(2.0) 路径长度为 2,根据第一原则,X(2.0) 会被解析使用; 2. A -> B -> Y(1.0),A -> C -> Y(2.0),Y(1.0) 和 Y(2.0) 的依赖路径长度相同,如果 B 的依赖声明在C之前,那么 Y(1.0) 会被解析使用。
5. 依赖相关的命令
- mvn dependency:list — 列出最终确定的依赖
- mvn dependency:tree — 打出依赖树
- mvn dependency:analyze — 依赖分析,给出项目中已使用但未声明的依赖,以及未使用但已声明的依赖
6. POM 优化
6.1. 依赖管理
- 多模块项目中,定义一个主 pom,在主 pom 中使用 dependencyManagement 定义依赖、版本和依赖排除
- 子模块从主 pom 中继承依赖,一般情况下,不要在子模块中做依赖排除,也不要指定依赖版本
6.2. 归类依赖
如果多个相关的依赖的版本都是相同的,可以使用 properties 元素定义 maven 属性,依赖的版本值用这一属性引用表示。
代码语言:javascript复制<properties>
<spring.version>2.5.6</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>
7. 参考资料
Maven实战(Maven in Action) Maven权威指南。 Maven重要概念及最佳实践 — https://segmentfault.com/a/1190000000640821。 Maven依赖范围及传递 — https://blog.csdn.net/stypace/article/details/38440545