超级详细的Maven教程(五)依赖管理

简介: 我们知道,在Maven的生命周期中,存在编译、测试、运行等过程,那么有些依赖只用于测试,比如junit;有些依赖编译用不到,只有运行的时候才能用到,比如mysql的驱动包在编译期就用不到(编译期用的是JDBC接口),而是在运行时用到的;还有些依赖,编译期要用到,而运行期不需要提供,因为有些容器已经提供了,比如servlet-api在tomcat中已经提供了,我们只需要的是编译期提供而已。

前面我们讲了maven项目中的最重要的文件:pom.xml 配置文件相关内容。介绍了pom 是如何定义项目,如何添加依赖的jar 包的等。


我们知道,在Maven的生命周期中,存在编译、测试、运行等过程,那么有些依赖只用于测试,比如junit;有些依赖编译用不到,只有运行的时候才能用到,比如mysql的驱动包在编译期就用不到(编译期用的是JDBC接口),而是在运行时用到的;还有些依赖,编译期要用到,而运行期不需要提供,因为有些容器已经提供了,比如servlet-api在tomcat中已经提供了,我们只需要的是编译期提供而已。


那么Maven是如何管理各个jar包的依赖关系,jar包之间的依赖传递和依赖冲突呢?所以接下来就讲一讲maven的依赖管理。

 

一、声明依赖

Maven 项目使用pom.xml 文件,可以方便的管理项目中所有依赖的jar包,之前也介绍过pom.xml 文件是如何定义依赖的。下面就以一段 junit依赖声明,演示pom.xml 是如何定义相关依赖的:

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <type>jar</type>
        <scope>test</scope>
        <optional>false</optional>
        <exclusions>
            <exclusion></exclusion>
        </exclusions>
    </dependency>
</dependencies>

上面的示例我们定义了junit的依赖,而且junit只在测试阶段生效。具体配置参数如下:

  • type:依赖类型,对应构件中定义的 packaging,可不声明,默认为 jar;
  • scope:依赖范围,大致有compile、provided、runtime、test、system等几个;
  • optional:依赖是否可选;
  • exclusions:排除传递依赖。

总结说来,在pom.xml 配置文件中,配置节点引入了节点,它主要管理依赖的范围。大致有compile、provided、runtime、test、system等几个。引入 节点排除某些依赖。解决了控制各个jar包的依赖范围,jar包之间的依赖传递和依赖冲突的问题。

 

二、依赖范围

Maven在执行不同的命令时(mvn package,mvn test,mvn install ……),会使用不同的 classpath,Maven 对应的有三套 classpath,即:编译classpath、测试classpath,运行classpath。我们可在dependency中的scope元素中进行配置。从而决定了该依赖构件会被引入到哪一个 classpath 中。默认为:compile。Maven提供的依赖范围如下:

  • compile:编译依赖范围,默认值。此选项对编译、测试、运行三种 classpath 都有效,如 hibernate-core-3.6.5.Final.jar,表明在编译、测试、运行的时候都需要该依赖;
  • test:测试依赖范围。只对测试有效,表明只在测试的时候需要,在编译和运行时将无法使用该类依赖,如 junit;
  • provided:已提供依赖范围。编译和测试有效,运行无效。如 servlet-api ,在项目运行时,tomcat 等容器已经提供,无需 Maven 重复引入;
  • runtime:运行时依赖范围。测试和运行有效,编译无效。如 jdbc 驱动实现,编译时只需接口,测试或运行时才需要具体的 jdbc 驱动实现;
  • system:系统依赖范围。和 provided 依赖范围一致,也是编译和测试有效,需要通过  显示指定,且可以引用环境变量;
  • import:导入依赖范围。使用该选项,通常需要 pom,将目标 pom 的 dependencyManagement 配置导入合并到当前 pom 的 dependencyManagement 元素。

需要注意的是,在打包阶段,使用的是运行classpath。即引入到运行classpath中的Maven依赖会被一起打包。


三、依赖传递

所谓依赖传递,就是A依赖B,B依赖C,A就间接依赖C,那么A与C直接的依赖关系就叫传递性依赖。

直接依赖又叫第一直接依赖,传递性依赖又叫第二直接依赖,其中第一直接依赖和第二直接依赖的依赖范围,决定了传递性依赖的依赖范围。

下面使用hibernate-core的依赖关系详细介绍所谓的依赖传递:

image.png

如上图所示,hibernate-core 依赖 hibernate-commons-annotations ,而 hibernate-commons-annotations 又依赖 slf4j-api ,hibernate-core 对 slf4j-api 的依赖就是传递依赖。pom.xml 文件中我们只需要引入 hibernate-core 构件的依赖,Maven 会自动为我们引入依赖jar包及传递依赖的jar包,无需再手动引一遍相关依赖。所以不用担心依赖冲突的问题。

那么如何判定一个间接依赖是否有必要被引入呢?间接依赖被引入后其依赖范围又是什么呢?

其实很简单,就是通过第一直接依赖的依赖范围和第二直接依赖的依赖范围之间的关系,来判定是否有必要引入间接依赖以及确定引入间接依赖后其依赖范围,如下表所示:

image.png

所以,通过上表我们可以发现:

1)第二直接依赖的依赖范围为compile : 传递依赖的依赖范围同第一直接依赖的依赖范围一样。

2)第二直接依赖的依赖范围为test : 传递依赖不会被引入。

3)第二直接依赖的依赖范围为provided : 只有当第一直接依赖的依赖范围亦为provided时,传递依赖才会被引入,且依赖范围依然是provided。

4)第二直接依赖的依赖范围为runtime : 除了第一直接依赖的依赖范围为compile时传递依赖的依赖范围为runtime外,其余情况下,传递依赖的依赖范围同第一直接依赖的依赖范围一样。


四、依赖冲突

通常我们不需要关心传递性依赖,但是当多个传递性依赖中有对同一构件不同版本的依赖时,如何解决呢?通常我们有一下两个原则:

  • 短路径优先:假如有以下依赖:A -> B -> C ->X(版本 1.0) 和 A -> D -> X(版本 2.0),则优先解析较短路径的 X(版本 2.0);
  • 先声明优先:即谁先声明,谁被解析。


针对依赖冲突中的“短路径优先”,那如果我们就想使用长路径的依赖怎么办呢?这时可以使用依赖排除元素,显示排除短路径依赖。在非冲突的情况下,这种方法同样有效。


五、解决依赖冲突

一般情况下我们不需要关心依赖的问题。而当依赖出问题时,我们需要知道该如何解决依赖冲突。解决依赖冲突的方式如下:

(1)首先,使用:mvn dependency:tree 命令查看项目的依赖列表,

(2)然后,通过依赖列表,找出冲突的jar包。

(3)最后,可选依赖 option或是排除依赖 exclusions 处理相关冲突。


1.可选依赖 option

通过项目中的pom.xml中的dependency下的option元素中进行配置,只有显式地配置项目中某依赖的option元素为true时,该依赖才是可选依赖;不设置该元素或值为false时,作用是:当某个间接依赖是可选依赖时,无论依赖范围是什么,其都不会因为传递性依赖机制而被引入。

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
      <!-- optional=true,依赖不会传递,该项目依赖devtools;之后依赖boot项目的项目如果想要使用devtools,需要重新引入 -->
      <optional>true</optional>
    </dependency>

上面的示例,当optional=true,devtools的依赖不会传递,该项目依赖devtools;之后依赖该项目的项目则不会依赖devtools组件,如果想要使用devtools,需要重新引入。


2.排除依赖 exclusions

我们知道,由于Maven的的传递性依赖机,有时候第三方组件B的C依赖由于版本(1.0)过低时。我们期望能够将该间接依赖直接剔除出去,这样不会影响到项目中其他依赖组件。这时可以通过exclusions元素实现。

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <exclusions>
    <exclusion>
      <artifactId>commons-logging</artifactId>
      <groupId>commons-logging</groupId>
    </exclusion>
  </exclusions>
</dependency>

上面,我们排除了spring-core组件忠的commons-logging。


最后

以上,我们就把Maven的依赖关系,Jar包之间的依赖传递和依赖冲突介绍完了。

相关文章
|
4月前
|
Java Maven 容器
java依赖冲突解决问题之Maven在编译打包过程中对依赖的jar包如何解决
java依赖冲突解决问题之Maven在编译打包过程中对依赖的jar包如何解决
|
1月前
|
Java Maven
maven打瘦包,且只打入部分想打入的依赖瘦包
maven打瘦包,且只打入部分想打入的依赖瘦包 设计 工程结构分析 环境管理 城市资源 安全工程 工程管理
53 10
|
2月前
|
Java API Apache
除了 Maven,还有哪些工具可以管理项目的依赖和版本冲突
除了Maven,常用的项目依赖管理和版本冲突解决工具有Gradle、Ivy、Ant+Ivy、SBT等。这些工具各有特点,适用于不同的开发环境和需求。
143 2
|
2月前
|
XML 安全 Java
【Maven】依赖管理,Maven仓库,Maven核心功能
【Maven】依赖管理,Maven仓库,Maven核心功能
645 3
|
2月前
|
Java Maven
Maven 依赖管理
Maven 一个核心的特性就是依赖管理。当我们处理多模块的项目(包含成百上千个模块或者子项目),模块间的依赖关系就变得非常复杂,管理也变得很困难。针对此种情形,Maven 提供了一种高度控制的方法。
101 5
|
3月前
|
Java Maven
Maven 引入外部依赖
如果我们需要引入第三方库文件到项目,该怎么操作呢?
45 5
|
4月前
|
安全 Java Maven
优化Maven镜像配置:使用阿里云加速依赖下载
更新Maven镜像配置至关重要,尤其使用阿里云仓库时。在`settings.xml`中加入特定镜像配置可显著提升依赖下载速度。示例配置指定了阿里云镜像ID、替代表态仓库、安全的URL、默认布局及启用版本管理。需定位至用户目录下的`.m2/`文件夹编辑`settings.xml`,添加镜像信息后保存测试。若下载仍慢,考虑网络状况或备选镜像。多镜像设置时需注意避免冲突。
707 3
|
4月前
|
Java 测试技术 Maven
单元测试问题之在Maven项目中引入JUnit 5和Mockito的依赖如何解决
单元测试问题之在Maven项目中引入JUnit 5和Mockito的依赖如何解决
241 1
|
4月前
|
Java Maven
intellij idea如何查看项目maven依赖关系图
这篇文章介绍了如何在IntelliJ IDEA中查看项目的Maven依赖关系图,包括使用Maven工具栏和相关操作来展示和查看依赖细节。
|
4月前
|
Java Maven 开发者
"揭秘IDEA的神奇助手:Maven Helper插件,让你轻松驾驭复杂依赖,告别冲突噩梦!"
【8月更文挑战第20天】Maven Helper是一款提升Java开发者工作效率的IDEA插件,它能直观展示项目依赖关系并协助管理。主要功能包括依赖树视图、冲突检测与解决及依赖排除。安装简便,重启IDEA后即用。借助其“Dependencies”面板,开发者可以清晰了解依赖详情,快速定位并解决冲突问题,有效优化项目结构,提升开发效率。
277 0