Maven项目管理工具:Maven依赖传递

杰米粉2021-09-30 11:38

    Maven依赖传递是Maven的核心机制之一,它能够一定程度上简化Maven的依赖配置。本节我们将详细介绍依赖传递及其相关概念。

    依赖传递

    如下图所示,项目A依赖于项目B,B又依赖于项目C,此时B是A的直接依赖,C是A的间接依赖。

Maven项目管理工具:Maven依赖传递

    Maven的依赖传递机制是指:不管Maven项目存在多少间接依赖,POM中都只需要定义其直接依赖,不必定义任何间接依赖,Maven会动读取当前项目各个直接依赖的POM,将那些必要的间接依赖以传递性依赖的形式引入到当前项目中。Maven的依赖传递机制能够帮助用户一定程度上简化POM的配置。

    基于A、B、C三者的依赖关系,根据Maven的依赖传递机制,我们只需要在项目A的POM中定义其直接依赖B,在项目B的POM中定义其直接依赖C,Maven会解析A的直接依赖B的POM,将间接依赖C以传递性依赖的形式引入到项目A中。

    通过这种依赖传递关系,可以使依赖关系树迅速增长到一个很大的量级,很有可能会出现依赖重复,依赖冲突等情况,Maven针对这些情况提供了如下功能进行处理。

    依赖范围(Dependencyscope)

    依赖调解(Dependencymediation)

    可选依赖(Optionaldependencies)

    排除依赖(Excludeddependencies)

    依赖管理(Dependencymanagement)

    依赖范围

    首先,我们要知道Maven在对项目进行编译、测试和运行时,会分别使用三套不同的classpath。Maven项目构建时,在不同阶段引入到classpath中的依赖时不同的。例如编译时,Maven会将与编译相关的依赖引入到编译classpath中;测试时,Maven会将与测试相关的的依赖引入到测试classpath中;运行时,Maven会将与运行相关的依赖引入到运行classpath中。

    我们可以在POM的依赖声明使用scope元素来控制依赖与三种classpath(编译classpath、测试classpath、运行classpath)之间的关系,这就是依赖范围。

    Maven具有以下6中常见的依赖范围,如下表所示。    

依赖范围 描述
compile 编译依赖范围,scope 元素的缺省值。使用此依赖范围的 Maven 依赖,对于三种 classpath 均有效,即该 Maven 依赖在上述三种 classpath 均会被引入。例如,log4j 在编译、测试、运行过程都是必须的。
test 测试依赖范围。使用此依赖范围的 Maven 依赖,只对测试 classpath 有效。例如,Junit 依赖只有在测试阶段才需要。
provided  已提供依赖范围。使用此依赖范围的 Maven 依赖,只对编译 classpath 和测试 classpath 有效。例如,servlet-api 依赖对于编译、测试阶段而言是需要的,但是运行阶段,由于外部容器已经提供,故不需要 Maven 重复引入该依赖。
runtime  运行时依赖范围。使用此依赖范围的 Maven 依赖,只对测试 classpath、运行 classpath 有效。例如,JDBC 驱动实现依赖,其在编译时只需 JDK 提供的 JDBC 接口即可,只有测试、运行阶段才需要实现了 JDBC 接口的驱动。
system 系统依赖范围,其效果与 provided 的依赖范围一致。其用于添加非 Maven 仓库的本地依赖,通过依赖元素 dependency 中的 systemPath 元素指定本地依赖的路径。鉴于使用其会导致项目的可移植性降低,一般不推荐使用。
import 导入依赖范围,该依赖范围只能与 dependencyManagement 元素配合使用,其功能是将目标 pom.xml 文件中 dependencyManagement 的配置导入合并到当前 pom.xml 的 dependencyManagement 中。

    依赖范围与三种classpath的关系一览表,如下所示。

依赖范围 编译 classpath 测试 classpath 运行 classpath 例子
compile log4j
test - - junit
provided - servlet-api
runtime - - JDBC-driver
system - 非 Maven 仓库的本地依赖

    依赖范围对传递依赖的影响

    项目A依赖于项目B,B又依赖于项目C,此时我们可以将A对于B的依赖称之为第一直接依赖,B对于C的依赖称之为第二直接依赖。

    B是A的直接依赖,C是A的间接依赖,根据Maven的依赖传递机制,间接依赖C会以传递性依赖的形式引入到A中,但这种引入并不是无条件的,它会受到依赖范围的影响。

    传递性依赖的依赖范围受第一直接依赖和第二直接依赖的范围影响,如下表所示。

  compile test provided runtime
compile compile - - runtime
test test - - test
provided provided - provided provided
runtime runtime - - runtime

    注:上表中,左边第一列表示第一直接依赖的依赖范围,上边第一行表示第二直接依赖的依赖范围。交叉部分的单元格的取值为传递性依赖的依赖范围,若交叉单元格取值为“-”,则表示该传递性依赖不能被传递。

    通过上表,可以总结出以下规律:

    当第二直接依赖的范围是compile时,传递性依赖的范围与第一直接依赖的范围一致;

    当第二直接依赖的范围是test时,传递性依赖不会被传递;

    当第二直接依赖的范围是provided时,只传递第一直接依赖的范围也为provided的依赖,且传递性依赖的范围也为provided;

    当第二直接依赖的范围是runtime时,传递性依赖的范围与第一直接依赖的范围一致,但compile例外,此时传递性依赖的范围为runtime。

    依赖调节

    Maven的依赖传递机制可以简化依赖的声明,用户只需要关心项目的直接依赖,而不必关心这些直接依赖会引入哪些间接依赖。但当一个间接依赖存在多条引入路径时,为了避免出现依赖重复的问题,Maven通过依赖调节来确定间接依赖的引入路径。

    依赖调节遵循以下两条原则:

    1、引入路径短者优先

    2、先声明者优先

    以上两条原则,优先使用第一条原则解决,第一条原则无法解决,再使用第二条原则解决。

    引入路径短者优先

    引入路径短者优先,顾名思义,当一个间接依赖存在多条引入路径时,引入路径短的会被解析使用。

    例如,A存在这样的依赖关系:

    A->B->C->D(1.0)

    A->X->D(2.0)

    D是A的间接依赖,但两条引入路径上有两个不同的版本,很显然不能同时引入,否则造成重复依赖的问题。根据Maven依赖调节的第一个原则:引入路径短者优先,D(1.0)的路径长度为3,D(2.0)的路径长度为2,因此间接依赖D(2.0)将从A->X->D(2.0)路径引入到A中。

    先声明者优先

    先声明者优先,顾名思义,在引入路径长度相同的前提下,POM文件中依赖声明的顺序决定了间接依赖会不会被解析使用,顺序靠前的优先使用。

    例如,A存在以下依赖关系:

    A->B->D(1.0)

    A->X->D(2.0)

    D是A的间接依赖,其两条引入路径的长度都是2,此时Maven依赖调节的第一原则已经无法解决,需要使用第二原则:先声明者优先。

    A的POM文件中配置如下。

<dependencies>
    ...      
    <dependency>
        ...
        <artifactId>B</artifactId>       
        ...
    </dependency>
    ...
    <dependency>
        ...
        <artifactId>X</artifactId>
        ...
    </dependency>
    ...
</dependencies>

  有以上配置可以看出,由于B的依赖声明比X靠前,所以间接依赖D(1.0)将从A->B->D(1.0)路径引入到A中。

    以上就是小编为大家整理发布的“Maven项目管理工具:Maven依赖传递”一文,更多相关内容尽在开课吧广场Java教程频道。

Maven项目管理工具:Maven依赖传递

免责声明:本站所提供的内容均来源于网友提供或网络搜集,由本站编辑整理,仅供个人研究、交流学习使用。如涉及版权问题,请联系本站管理员予以更改或删除。
有用
分享
全部评论快来秀出你的观点
登录 后可发表观点…
发表
暂无评论,快来抢沙发!
高并发编程训练营