Maven的依赖传递
Maven的依赖传递是指,当一个项目依赖于另外一个项目时,Maven会自动将被依赖项目的依赖库也加入到当前项目的依赖库中。这样,我们就可以在当前项目中直接使用被依赖项目的依赖库,而不需要手动声明它们。
一、关键pom文件
1.1 super pom
super pom是Maven中所有pom.xml文件的默认父pom.xml文件,它定义了所有Maven项目都会继承的默认配置信息。当我们创建一个新的Maven项目时,如果没有指定父pom.xml文件,Maven会自动将super pom作为父pom.xml文件。这类似于Java创建类时都会默认继承Object类。
super pom的内容包含了一些默认的配置信息,例如:
groupId
:默认值为org.apache.maven。artifactId
:默认值为maven-model。version
:默认值为Maven的版本号。packaging
:默认值为jar。dependencies
:包含了一些常用的依赖库,例如JUnit、Log4j等。build
:包含了一些常用的构建插件,例如Maven Compiler Plugin、Maven Surefire Plugin等。
Maven的安装目录中的$M2_HOME/lib/maven-model-builder-*.jar
文件来查看super pom的内容。在这个jar包中,有一个super-pom.xml文件,就是Maven的默认super pom。
可以通过修改super pom来修改所有Maven项目的默认配置信息。但是,建议不要修改super pom,而是通过在pom.xml文件中声明<dependencyManagement>
元素来管理依赖库的版本号,以及通过在pom.xml文件中声明<pluginManagement>
元素来管理构建插件的配置信息。
1.2 effective pom
Maven在处理pom.xml文件时,将所有继承关系、依赖关系、插件配置等信息合并后生成的最终pom.xml文件。这个最终的pom.xml文件包含了所有继承自父pom.xml文件的配置信息,以及所有依赖库的版本号、依赖范围等信息。
可以通过Maven命令mvn help:effective-pom
来查看当前项目的effective pom。执行该命令后,Maven会将当前项目的所有配置信息打印到控制台上,包括所有继承自父pom.xml文件的配置信息,以及所有依赖库的版本号、依赖范围等信息。
二、依赖范围
Maven中的依赖范围(scope)用来控制依赖库在不同阶段的使用范围。Maven中定义了以下几种依赖范围:
compile
:默认的依赖范围,表示依赖库在编译、测试、运行时都可用。provided
:表示依赖库在编译和测试时可用,但在运行时不可用,因为它会由JDK或容器提供。runtime
:表示依赖库在测试和运行时可用,但在编译时不可用。test
:表示依赖库只在测试时可用,不会被打包到最终的应用程序中。system
:表示依赖库在编译和测试时可用,但不会从Maven仓库中下载,需要手动指定路径。import
:表示依赖库的作用类似于<dependencyManagement>
元素,用来管理依赖库的版本号。
在pom.xml文件中,我们可以使用<scope>
元素来声明依赖库的范围,例如JUnit测试相关的依赖范围是<scope>runtime</scope>
,合理使用依赖范围可以避免不必要的依赖库加载,减少应用程序的体积,提高应用程序的性能。
三、依赖调节
随着项目工程层级和所需依赖的增加,依赖树也在不断膨胀,为了简化依赖和让开发者尽可能只关心直接依赖,同时在一个间接依赖存在多个引入路径时不会出现依赖重复的问题,Maven的依赖调节遵循以下两条原则:
- 引入路径短者优先
- 先声明者优先
以上两条原则,优先使用第一条原则解决,第一条原则无法解决,再使用第二条原则解决。
3.1 引入路径短者优先
当一个间接依赖存在多条引入路径时,引入路径短的会被解析使用。
例如,A 存在这样的依赖关系:
依赖1:A->B->C->D(1.0)
依赖2:A->X->D(2.0)
D是A的间接依赖,存在两个版本的D,依赖1的路径长度为3,依赖2的长度为2,最终间接依赖 D(2.0)将从 A->X->D(2.0) 路径引入到 A 中。
3.2 先声明者优先
在引入路径长度相同的前提下,POM 文件中依赖声明的顺序决定了间接依赖会不会被解析使用,顺序靠前的优先使用。
例如,A 存在以下依赖关系:
A->B->D(1.0)
A->X->D(2.0)
D 是 A 的间接依赖,其两条引入路径的长度都是 2,此时 Maven 依赖调节的第一原则已经无法解决,需要使用第二原则:先声明者优先。如果 B 的依赖先在依赖中声明,则D(1.0)会被最终引入到 A 中,在POM中的代码如下:
<dependencies>
...
<dependency>
...
<artifactId>B</artifactId>
...
</dependency>
...
<dependency>
...
<artifactId>X</artifactId>
...
</dependency>
...
</dependencies>
四、依赖管理的关键标签
4.1 parent
parent标签指定当前项目以另一个项目为父项目,一般场景是多个项目需要指定同一个副项目,如果只是一对一的依赖,直接使用dependency就可以了。
parent类似与基类,它定义了一个Maven项目的基本信息,包括groupId、artifactId、version等。当一个项目需要继承另一个项目的基本信息时,可以在子项目的pom.xml文件中使用parent元素来引用父项目的POM文件。这样,子项目就可以继承父项目的基本信息,包括依赖管理、插件管理等。
创建一个parent项目,打包类型为pom(parent项目只能是pom,不包含任何代码),parent项目中不存放任何代码,只是管理多个项目之间公共的依赖。
super-pom.xml是Maven中所有pom.xml文件的默认父pom.xml文件
4.2 dependencyManagement
dependencyManagement是一个机制,它可以让多个模块共享同一个依赖版本号,从而避免版本冲突和重复依赖的问题。
具体来说,当一个项目中有多个模块时,每个模块都可以声明自己的依赖,但是如果每个模块都声明了不同版本的同一个依赖,就会导致版本冲突和重复依赖的问题。为了解决这个问题,可以在父模块的pom.xml文件中使用dependencyManagement元素来声明依赖的版本号,然后在子模块中引用这个版本号即可。
使用dependencyManagement的好处是,可以让项目中的所有模块都使用相同的依赖版本号,从而避免版本冲突和重复依赖的问题。此外,还可以方便地升级依赖版本号,只需要修改父模块的pom.xml文件即可,而不需要修改每个子模块的pom.xml文件。
总的来说,parent和dependencyManagement都是用于管理依赖的机制,但它们的作用不同。parent用于定义项目的基本信息,dependencyManagement用于管理依赖版本号,一般会有专门的parent POM进行dependencyManagement的相关配置。在实际开发中,可以根据需要使用这两个机制来管理依赖。
4.3 排除依赖 exclusions
排除依赖是使用 exclusions 元素实现的,该元素下可以包含若干个 exclusion 子元素,用于排除若干个间接依赖
- 排除依赖是控制当前项目是否使用其直接依赖传递下来的间接依赖;
- exclusions 元素下可以包含若干个 exclusion 子元素,用于排除若干个间接依赖;
- exclusion 元素用来设置具体排除的间接依赖,该元素包含两个子元素:groupId 和 artifactId,用来确定需要排除的间接依赖的坐标信息;
- exclusion 元素中只需要设置 groupId 和 artifactId 就可以确定需要排除的依赖,无需指定版本 version。
4.4 可选依赖 optional
依赖声明中使用 optional
元素,将其设置成可选依赖,示例配置如下:
<dependencies>
<dependency>
<groupId>com.mytest.common</groupId>
<artifactId>test-zxy</artifactId>
<version>1.0-SNAPSHOT</version>
<!--设置可选依赖 -->
<optional>true</optional>
</dependency>
</dependencies>
关于 optional
元素及可选依赖说明如下:
- 可选依赖用来控制当前依赖是否向下传递成为间接依赖;
optional
默认值为false
,表示可以向下传递称为间接依赖;optional
取值为true
,则表示当前依赖不能向下传递成为间接依赖。