Maven 的坐标和依赖
Maven 坐标
何为 Maven 坐标
因为在 Maven 的世界中拥有数量非常巨大的构件,也就是平时我们用的一些 jar、war等文件,所以 Maven 就定义了一组规则:任何一个构件都可以使用 Maven 坐标唯一标识, Maven 坐标的元素包括 groupId、artifactId、version、packaging、classifier。
坐标详解
- groupId: 定义当前 Maven 项目隶属的实际项目,groupId 的表示方式与 Java 包名的表示方式类似,通常与域名反向一一对应。
- artifactId: 该元素定义实际项目中的一个 Maven 项目(模块),推荐的做法是使用实际项目名作为 artifactId 的前缀。比如 spring-core。
- version: 该元素定义了 Maven 项目当前所处的版本。
- packaging: 该元素定义 Maven 项目的打包方式。默认为 jar。
- classifier: 该元素用户帮助定义构建输出的一些附属构件。注意,不能直接定义项目的 classifier,因为附属构件不是项目直接默认生成的,而是由附加插件帮助生成。
以上5个元素中,groupId、artifactId、version 是必须要定义的,packaging 是可选的(默认值为 jar),而 classifier 是不能直接定义的。
Maven 依赖
依赖配置
<project> <dependencies> <dependency> <groupId>...</groupId> <artifactId>...</artifactId> <version>...</version> <type>...</type> <scope>...</scope> <optional>...</optional> <exclusions> <exclusion> ... </exclusion> ... </exclusions> </dependency> ... </dependencies> </project>
- groupId、artifactId 和 version: 依赖的基本坐标, Maven 根据坐标才能找到需要的依赖。
- type: 依赖的类型,该元素的默认值为 jar,大部分情况下无需声明。
- scope: 依赖的范围。
- optional: 标记依赖是否可选。如果该属性值为 true,当其它项目依赖于本项目时,对应的构件将不会被会传递。
- exclusions: 用来排除传递性依赖。
依赖范围
在 Maven 项目中有三种 classpath: 编译 classpath、测试 classpath、运行 classpath, Maven 的依赖范围就是用来控制依赖与这三种 classpath 的关系的。 Maven 有以下几种依赖范围:
- compile: 编译依赖范围。也是 Maven 的默认依赖范围,它对于编译、测试、运行三种 classpath 都有效。
- test: 测试依赖范围。只对于测试 classpath 有效。
- provided: 已提供依赖范围。对于编译和测试 classpath 有效,但在运行时无效。
- runtime: 运行时依赖范围。对于测试和运行 classpath 有效,但在编译主代码时无效。
- system: 系统依赖范围。该依赖与三种 classpath 的关系和 provided 依赖范围完全一至,使用该范围时必须要通过 systemPath 元素显式地指定依赖文件的路径,systemPath 元素可以引用环境变量。如:
<dependency> <groupId>...</groupId> <artifactId>...</artifactId> <version>...</version> <systemPath>${java_home}/lib/rt.jar</systemPath> </dependency>
- import (Maven 2.0.9+): 导入依赖范围。该依赖范围不会对 classpath 产生实际影响。
依赖范围与 classpath 的关系
依赖范围 对编译 classpath 有效 对测试 classpath 有效 有运行时 classpath 有效 例子 compile Y Y Y spring-core test - Y - JUnit provided Y Y - servlet-api runtime - Y Y JDBC 驱动实现 system Y Y - 本地 jar 传递性依赖
Maven 的传递性依赖很好的解决了手动管理依赖的依赖问题,让我们只关心直接依赖。假设 A 依赖于 B,B 又依赖于 C,那么 A 对于 B 就是第一直接依赖,B 对于 C 是第二直接依赖,A 对于 C 是传递性依赖。所以 Maven 的依赖范围不仅能控制依赖与三种 classpath 的关系,还对传递性依赖产生影响。第一直接依赖范围和第二直接依赖范围决定了传递性依赖的范围。如下表,最左边一列表示第一直接依赖,最上面一行表示第二直接依赖
(-_-) compile test provided runtime compile compile - - runtime test test - - test provided privided - provided provided runtime runtime - - runtime 依赖调解
Maven 的传递性依赖有好处但也可能造成问题,同一构件引入多次且版本还不一致。所以就出现了依赖调解的两个原则:
- 路径最近的优先。如 A -> B -> C -> X(1.0), A -> D -> X(2.0),此时 X 是 A 的传递性依赖,但这两条依赖路径上有两个版本的 X,而 X(1.0) 的路径长度为3,X(2.0)的路径长度为2,应用本原则后 X(2.0) 会被解析使用。
- 第一声明者优先。当多个路径长度相等的前提下优先解析使用在 POM 中声明最靠前的依赖。
排除传递性依赖
如果在特定的情况下你不想引入传递性依赖而是自己显示的声明就可以用 元素排除依赖。如下:
<dependency> <groupId>...</groupId> <artifactId>...</artifactId> <version>...</version> <exclusions> <exclusion> <groupId>...</groupId> <artifactId>...</artifactId> </exclusion> ... </exclusions> </dependency>