依赖传递 & 依赖范围 & 依赖调节

依赖传递

image
基于 A、B、C 三者的依赖关系,根据 Maven 的依赖传递机制,我们只需要在项目 A 的 POM 中定义其直接依赖 B ,在项目 B 的 POM 中定义其直接依赖 C ,Maven 会解析 A 的直接依赖 B 的 POM ,将间接依赖 C 以传递性依赖的形式引入到项目 A 中。
通过这种依赖传递关系,可以使依赖关系树迅速增长到一个很大的量级,很有可能会出现依赖重复,依赖冲突等情况,Maven 针对这些情况提供了如下功能进行处理。

  • 依赖范围(Dependency scope)
  • 依赖调解(Dependency mediation)
  • 可选依赖(Optional dependencies)
  • 排除依赖(Excluded dependencies)
  • 依赖管理(Dependency management)

依赖范围

首先,我们要知道 Maven 在对项目进行编译、测试和运行时,会分别使用三套不同的 classpath 。Maven 项目构建时,在不同阶段引入到 classpath 中的依赖是不同的。例如编译时,Maven 会将与编译相关的依赖引入到编译 classpath 中;测试时,Maven 会将与测试相关的的依赖引入到测试 classpath 中;运行时,Maven 会将与运行相关的依赖引入到运行 classpath 中。
我们可以在 POM 的依赖声明使用 scope 元素来控制依赖与三种 classpath(编译 classpath 、测试 classpath 、运行 classpath )之间的关系,这就是依赖范围。Maven 具有以下 6 中常见的依
image
依赖范围与三种 classpath 的关系一览表如下所示:
image

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

项目 A 依赖于项目 B ,B 又依赖于项目 C ,此时我们可以将 A 对于 B 的依赖称之为第一直接依赖,B 对于 C 的依赖称之为第二直接依赖。B 是 A 的直接依赖,C 是 A 的间接依赖,根据 Maven 的依赖传递机制,间接依赖 C 会以传递性依赖的形式引入到 A 中,但这种引入并不是无条件的,它会受到依赖范围的影响。传递性依赖的依赖范围受第一直接依赖和第二直接依赖的范围影响,如下表所示。
image
注:上表中,左边第一列表示第一直接依赖的依赖范围,上边第一行表示第二直接依赖的依赖范围。交叉部分的单元格的取值为传递性依赖的依赖范围,若交叉单元格取值为“-”,则表示该传递性依赖不能被传递。
通过上表,可以总结出以下规律:

  • 当第二直接依赖的范围是 compile 时,传递性依赖的范围与第一直接依赖的范围一致;
  • 当第二直接依赖的范围是 test 时,传递性依赖不会被传递;
  • 当第二直接依赖的范围是 provided 时,只传递第一直接依赖的范围也为 provided 的依赖,且传递性依赖的范围也为 provided;
  • 当第二直接依赖的范围是 runtime 时,传递性依赖的范围与第一直接依赖的范围一致,但 compile 例外,此时传递性依赖的范围为 runtime。

依赖调节

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

  • 引入路径短者优先
  • 先声明者优先
    以上两条原则,优先使用第一条原则解决,第一条原则无法解决,再使用第二条原则解决。

引入路径短者优先

引入路径短者优先,顾名思义,当一个间接依赖存在多条引入路径时,引入路径短的会被解析使用。
例如,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 中。

posted @ 2023-01-10 00:00  HopeLive  阅读(64)  评论(0)    收藏  举报