关于Maven中的依赖问题

前言

这个月跟着老师开发项目的时候,利用Maven来构建项目,遇到了依赖冲突的问题(阿里的EasyExcel与github上的一个将excel转pdf的插件都用到了poi,且两个poi版本互不兼容),也因此把Maven的依赖机制好好地学习了一遍,在此做一个记录。

什么是依赖

依赖可以理解为一个软件包,由groupIdartifactIdversion 组成的坐标进行唯一确定,它在pom.xml中常以下面这种形式存在:

<!--这里以引入easyExcel为例子-->
 <dependencies>
 	 <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.6</version>
        </dependency>
 </dependencies>

实际上,完整的依赖描述是这样的

 <dependencies>
 	<dependency>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <version>...</version>
            <scope>...</scope>
            <optional>...</optional>
            <exclusions>
            	<exclusion>...</exclusion>
            </exclusions>
        </dependency>
 </dependencies>

依赖范围

从上面的例子中,我们看到标准的依赖写法中有一个scope标签对,这个标签决定了该依赖的作用范围,下面对其进行逐一解释。

compile

默认的依赖范围

编译测试运行

test

顾名思义,属于test的依赖范围只在测试时有效,典型的例子是各种测试框架

编译测试运行
--

举例:

 <dependency>
    <groupId>ch.qos.logback</groupId>
     <artifactId>logback-classic</artifactId>
     <version>1.2.3</version>
     <scope>test</scope>
</dependency>

provided

provided在英语中是已提供的意思,也就是说在什么情况下,某个软件包已经提供了,我们不需要再次引入。一个典型的例子是servlet-api,在编译和测试时需要,但是在运行时,例如像tomcat这样的容器已经提供,就不需要引入了。

编译测试运行
-

举例:

<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
    <scope>provided</scope>
</dependency>

runtime

runtime的依赖范围表示在运行和测试时需要,编译时不需要。一个典型的例子是各种数据库驱动,只有在测试和运行的时候,才需要加载数据库驱动,而在编写代码的时候是不需要的。

编译测试运行
-

system

System的依赖范围与provided一致,但是在使用System依赖范围时,需要显示指定依赖文件的路径。

由于此类依赖不是Maven解析的,而且往往与本机系统绑定,可能造成不可移植的问题。

编译测试运行
-

举例:

<!-- 引入本地jar包-->
  <dependency>
            <groupId>com.github.caryyu</groupId>
            <artifactId>excel2pdf</artifactId>
            <version>1.0</version>
            <scope>system</scope>
            <systemPath>${basedir}/lib/excel2pdf-1.0.jar</systemPath>
        </dependency>

传递依赖

什么是传递依赖

如果软件包A依赖软件包B,同时,软件包B依赖软件包C,那么软件包A间接依赖了软件包C,它们构成了传递依赖。

举一个例子,图取自《Maven实战》,如下图所示:
在这里插入图片描述
软件包account-email依赖spring-corespring-core依赖commons-logging,那么,account-emailcommons-logging`构成传递依赖。

传递依赖的范围

在传递依赖中,每个软件包的依赖范围是不一样的,那么经过传递依赖后,最终会是什么依赖范围呢?这里有一张表格(表格取自《Maven实战》),其中最上边一行表示第一直接依赖,最左边一行表示第二直接依赖。

compiletestprovidedruntime
compilecompile--runtime
testtest--test
providedprovided-providedprovided
runtimeruntime--runtime

举一个例子,还是上面那张图,spring-core的依赖范围是compile,为第一直接依赖,commons-logging的依赖范围为test,为第二直接依赖。查表得知,最终account-email会以test的依赖范围引入commons-logging

上面的表格可以拆解成两个部分来看

  • 如果第一直接依赖是runtime,则依赖会得以传递,而且,第二直接依赖是什么,传递后的依赖也就是什么,注:第二直接依赖为compile时为runtime。
  • 如果第一直接依赖为除了runtime以外的其他依赖,那么对于第二直接依赖来说,限制条件更严格的依赖将不会得到传递。
    例如,对于provided依赖,test是比其限制条件更多的依赖,那么当第二直接依赖为provided,第一直接依赖为compile、provided时,依赖将得到传递,而第一直接依赖为test时,则不会。

依赖的调解

在Maven中,依赖有两种调解方式,即依赖调解解决的是当存在同一软件包的不同版本时,如何进行选择的问题。

路径最近者优先

如果项目A有这样的依赖:
A->B->E->C(1.0) --(1),
A->D->C(2.0) --(2)
其中,A项目所依赖的项目B和D都依赖项目C,但是是不同的版本,(1)路径长度为3,(2)路径长度为2,根据路径最短原则,C软件包的2.0版本将被引入。

第一声明者优先

当产生歧义的软件包到项目跟路径的路径长度都一致时,将使用该原则。第一声明者优先原则指出,当路径长度一致时,最先声明的软件包将被引入。

如何避免依赖冲突

一种方式是使用exclusions标签来进行排除依赖,这么做的好处如下:

  • 如果项目中有多个项目依赖了同一个软件包的不同版本,使用排除依赖,可以人工的指定我们需要留下哪一个版本
  • 使用排除依赖,可以排除掉一些SNAPSHOT依赖,使项目更稳定。

例如在SpringBoot的pom中,对于测试依赖,有如下定义:

   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

网上搜索了一下为什么要进行排除依赖,一种说法是:junit-vintage-engine用于运行JUnit4测试,而在依赖树中的junit-jupiter-engine是运行JUnit5测试,一个项目中要么是Junit4,要么是JUnit5,只能二选一,因此进行依赖排除,避免依赖冲突。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值