1 Maven 依赖POM配置
<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 为依赖的范围,默认为compile;optional 用来标记依赖是否可选;exclusions 用来排除传递性依赖。
2 依赖范围
依赖范围,正是其中 scope 元素所能选择的内容。在正式介绍依赖范围之前,我们需要知道一件事,那就是:Maven 项目的环境变量(classpath)有三种,分别为编译时用的环境变量、测试时用的环境变量和运行时用的环境变量。而依赖范围就是用来控制依赖与这三种环境的关系的:
- compile:编译依赖范围,此为默认值,对编译、测试和运行三种环境变量都有效。
- test:测试依赖范围,仅对测试环境变量有效。
- provided:已提供的依赖范围,对编译和测试环境变量都有效。
- runtime:运行依赖范围,对测试和运行环境变量都有效。
- system:系统依赖范围,对编译和测试环境变量都有效,但由于此依赖不是通过 Maven 仓库解析的,而且往往与本机系统绑定,可能造成构建的不可移植,因此应该谨慎使用。
- import:导入依赖范围,并不会对上面的三种环境变量产生实际的影响。
上表为 Maven 的直接依赖范围。除此之外,还有传递性依赖,那么何为传递性依赖呢?
在默认依赖范围的情况下,如果项目 A 依赖 B,B 依赖 C,那么 A 就依赖 C,这时 A 与 C 就是传递性依赖的关系。而且我们称 A 对 B 的依赖为第一直接依赖,B 对 C 的依赖为第二直接依赖,以此类推。
至于为什么要强调在默认依赖范围的情况下,则是传递性依赖也是有条件的,具体如下表所示:
观察上表,我们会发现:当第二直接依赖为compile时,则依赖关系以第一直接依赖为准;当第二直接依赖为test时,则没有依赖关系;当第二直接依赖为provided时,只有第一直接依赖也为provided时才有provided范围的依赖关系;当第二直接依赖为runtime时,除了第一直接依赖关系为compile时依赖范围为runtime,其他三种皆与第一直接依赖范围相同。
3 依赖原则
maven对于依赖相同的资源,默认会做出以下优化:
3.1 第一原则:以短路径长度为准
例如,项目A有这样的依赖关系 : A–>B–>C–>X(1.0)、A–>D–>X(2.0),X是A的传递性依赖,但是两条依赖路径上有两个版 本的X,那么哪个X会被maven解析使用呢?两个版本都被解析显然是不对的,因为那会造成依赖重复,因此必须选择一个。maven依赖调解的第一原则:路径最近者优先。该例中X(1.0)的路径长度为3,而X(2.0)的路径长度为2,因此X(2.0)会被解析使用。
3.2 第二原则:相同路径长度时,以pom中声明顺序先者为准
依赖调解第一原则不能解决所有问题,比如这样的依赖关系:A–>B–>Y(1.0),A–>C–>Y(2.0),Y(1.0)和Y(2.0)的依赖路径长度是一样的,都为2。那么到底谁会被解析使用呢?在maven2.0.8及之前的版本中,这是不确定的,但是maven2.0.9开始,为了尽可能避免构建的不确定性,maven定义了依赖调解的第二原则:第一声明者优先。在依赖路径长度相等的前提下,在POM中依赖声明的顺序决定了谁会被解析使用。顺序最靠前的那个依赖优胜。
4 依赖冲突解决
4.1 何为依赖冲突
Maven是个很好用的依赖管理工具,但是再好的东西也不是完美的。Maven的依赖机制会导致Jar包的冲突。举个例子,现在你的项目中,使用了两个Jar包,分别是A和B。现在A需要依赖另一个Jar包C,B也需要依赖C。但是A依赖的C的版本是1.0,B依赖的C的版本是2.0。这时候,Maven会将这1.0的C和2.0的C都下载到你的项目中,这样你的项目中就存在了不同版本的C,这时Maven会依据依赖路径最短优先原则,来决定使用哪个版本的Jar包,而另一个无用的Jar包则未被使用,这就是所谓的依赖冲突。
在大多数时候,依赖冲突可能并不会对系统造成什么异常,因为Maven始终选择了一个Jar包来使用。但是,不排除在某些特定条件下,会出现类似找不到类的异常,所以,只要存在依赖冲突,在我看来,最好还是解决掉,不要给系统留下隐患。
4.2 解决思路
- 声明优先原则
- 就近优先原则
- 排除依赖
4.3 解决方法
解决依赖冲突的方法,就是使用Maven提供的标签,标签需要放在标签内部,就像下面这样:
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.10.0</version>
<exclusions>
<exclusion>
<artifactId>log4j-api</artifactId>
<groupId>org.apache.logging.log4j</groupId>
</exclusion>
</exclusions>
</dependency>
4.4 使用Maven Helper解决Maven依赖冲突
如何才能知道自己的项目中哪些依赖的Jar包冲突了呢?Maven Helper这个InteliJ IDEA的插件帮我们解决了这个问题。插件的安装方法我就不讲了,既然你都会Maven了,我相信你也是会安装插件的。
在插件安装好之后,我们打开pom.xml文件,在底部会多出一个Dependency Analyzer选项
点开这个选项
找到冲突,点击右键,然后选择Exclude即可排除冲突版本的Jar包。