详解maven依赖问题

约定配置

Maven 提倡使用一个共同的标准目录结构,Maven 使用约定优于配置的原则,大家尽可能的遵守这样的目录结构,如下所示:
在这里插入图片描述

basedir 可以通过:System.getProperty('basedir') 获取到变量的值

maven他自己就知道你项目的源码、资源、测试代码、打包输出的位置,这些都是maven规定好的.

pom文件

POM( Project Object Model,项目对象模型 ) 是 Maven 工程的基本工作单元,是一个XML文件,包含了项目的基本信息,用于描述项目如何构件,声明项目依赖,等等。

执行任务或目标时,Maven 会在当前目录中查找 POM。它读取 POM,获取所需的配置信息,然后执行目标。

POM 中可以指定以下配置

  • 项目依赖
  • 插件
  • 执行目标
  • 项目构件 profile
  • 项目版本
  • 项目开发者列表
  • 相关邮件列表信息

maven座标

  • goupId:定义当前构件所属的组,通常与域名反向一一对应。
  • artifactId:项目组中构件的编号。
  • version:当前构件的版本号,每个构件可能会发布多个版本,通过版本号来区分不同版本的构件。
  • package:定义该构件的打包方式,比如我们需要把项目打成jar包,采用java -jar去运行这个jar包,那这个值为jar;若当前是一个web项目,需要打成war包部署到tomcat中,那这个值就是war,可选(jar、war、ear、pom、maven-plugin),比较常用的是jar、war、pom,packeage可以省略,默认为jar

maven导入依赖的构件

  • dependencies元素中可以包含多个dependency,每个dependency就表示当前项目需要依赖的一个构件的信息
  • dependency中groupId、artifactId、version是定位一个构件必须要提供的信息,所以这几个是必须的
  • type:依赖的类型,表示所要依赖的构件的类型,对应于被依赖的构件的packaging。大部分情况下,该元素不被声明,默认值为jar,表示被依赖的构件是一个jar包。
  • scope:依赖的范围,后面详解
  • option:标记依赖是否可选,后面详解
  • exclusions:用来排除传递性的依赖

maven依赖范围(scope)

scope是用来控制被依赖的构件与classpath的关系(编译、打包、运行所用到的classpath),scope有以下几种值:

  • compile
    编译依赖范围,如果没有指定,默认使用该依赖范围,对于编译源码、编译测试代码、测试、运行4种classpath都有效,比如上面的spring-web。

  • test
    测试依赖范围,使用此依赖范围的maven依赖,只对编译测试、运行测试的classpath有效,在编译主代码、运行项目时无法使用此类依赖。比如junit,它只有在编译测试代码及运行测试的时候才需要。

  • provide
    已提供依赖范围。表示项目的运行环境中已经提供了所需要的构件,对于此依赖范围的maven依赖,对于编译源码、编译测试、运行测试中classpath有效,但在运行时无效。比如上面说到的servlet-api,这个在编译和测试的时候需要用到,但是在运行的时候,web容器已经提供了,就不需要maven帮忙引入了。

  • runtime
    运行时依赖范围,使用此依赖范围的maven依赖,对于编译测试、运行测试和运行项目的classpath有效,但在编译主代码时无效,比如jdbc驱动实现,运行的时候才需要具体的jdbc驱动实现。

  • system
    系统依赖范围,该依赖与3中classpath的关系,和provided依赖范围完全一致。但是,使用system范围的依赖时必须通过systemPath元素显示第指定依赖文件的路径。这种依赖直接依赖于本地路径中的构件,可能每个开发者机器中构件的路径不一致,所以如果使用这种写法,你的机器中可能没有问题,别人的机器中就会有问题,所以建议谨慎使用

依赖范围与classpath的关系如下:
在这里插入图片描述

scope如果对于运行范围有效,意思是指依赖的jar包会被打包到项目的运行包中,最后运行的时候会被添加到classpath中运行。如果scope对于运行项目无效,那么项目打包的时候,这些依赖不会被打包到运行包中。

依赖的传递

只引入了spring-web依赖,而spring-web又依赖了spring-beans、spring-core,依赖也被自动加进来了,这种叫做依赖的传递。

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.1.0.RELEASE</version>
</dependency>

在这里插入图片描述

scope元素的值会对这种传递依赖会有影响

假设A依赖于B,B依赖于C,我们说A对于B是第一直接依赖,B对于C是第二直接依赖,而A对于C是传递性依赖,而第一直接依赖的scope和第二直接依赖的scope决定了传递依赖的范围,即决定了A对于C的scope的值。

下面我们用表格来列一下这种依赖的效果,表格最左边一列表示第一直接依赖(即A->B的scope的值),而表格中的第一行表示第二直接依赖(即B->C的scope的值),行列交叉的值显示的是A对于C最后产生的依赖效果。
在这里插入图片描述

  1. 比如A->B的scope是compile,而B->C的scope是test,那么按照上面表格中,对应第2行第3列的值-,那么A对于C是没有依赖的,(即C 不会传递给A),A对C的依赖没有从B->C传递过来,所以A中是无法使用C的
  2. 比如A->B的scope是compile,而B->C的scope是runtime,那么按照上面表格中,对应第2行第5列的值为runtime,那么A对于C是的依赖范围是runtime,表示A只有在运行的时候C才会被添加到A的classpath中,即对A进行运行打包的时候,C会被打包到A的包中
  3. 大家仔细看一下,上面的表格是有规律的,当B->C依赖是compile的时候(表中第2列),那么A->C的依赖范围和A->B的sope是一样的;当B->C的依赖是test时(表中第3列),那么B->C的依赖无法传递给A;当B->C的依赖是provided(表第4列),只传递A->C的scope为provided的情况,其他情况B->C的依赖无法传递给A;当B->C的依赖是runtime(表第5列),那么C按照B->C的scope传递给A

maven依赖调解功能

  1. 现实中可能存在这样的情况,
    A->B->C->Y(1.0),
    A->D->Y(2.0),
    此时Y出现了2个版本,1.0和2.0,此时maven会选择Y的哪个版本?
  • 路径最近原则
    上面
    A->B->C->Y(1.0),
    A->D->Y(2.0),
    Y的2.0版本距离A更近一些,所以maven会选择2.0。
  1. 但是如果出现了路径是一样的,如:
    A->B->Y(1.0),
    A->D->Y(2.0),
    此时maven又如何选择呢?
  • 最先声明原则
    如果出现了路径一样的,此时会看A的pom.xml中所依赖的B、D在dependencies中的位置,谁的声明在最前面,就以谁的为主
    比如A->B在前面,那么最后Y会选择1.0版本。

可选依赖(optional元素)

A->B中scope:compile
B->C中scope:compile
按照上面介绍的依赖传递性,C会传递给A,被A依赖。

假如B不想让C被A自动依赖,可以怎么做呢?
dependency元素下面有个optional,是一个boolean值,表示是一个可选依赖,B->C时将这个值置为true,那么C不会被A自动引入。

排除依赖

A项目的pom.xml中

<dependency>
    <groupId>com.java</groupId>
    <artifactId>B</artifactId>
    <version>1.0</version>
</dependency>

B项目1.0版本的pom.xml中

<dependency>
    <groupId>com.java</groupId>
    <artifactId>C</artifactId>
    <version>1.0</version>
</dependency>

上面A->B的1.0版本,B->C的1.0版本,而scope都是默认的compile,根据前面讲的依赖传递性,C会传递给A,会被A自动依赖,但是C此时有个更新的版本2.0,A想使用2.0的版本,此时A的pom.xml中可以这么写:

<dependency>
    <groupId>com.java</groupId>
    <artifactId>B</artifactId>
    <version>1.0</version>
    <exclusions>
        <exclusion>
            <groupId>com.java</groupId>
            <artifactId>C</artifactId>
        </exclusion>
    </exclusions>
</dependency>

上面使用使用exclusions元素排除了B->C依赖的传递,也就是B->C不会被传递到A中。

exclusions中可以有多个exclusion元素,可以排除一个或者多个依赖的传递,声明exclusion时只需要写上groupId、artifactId就可以了,version可以省略。

参考

详解maven解决依赖问题

发布了19 篇原创文章 · 获赞 2 · 访问量 1412
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章