5.理解maven的各种复杂依赖管理机制

1. 前提说明

我们知道,用可以引用任何你需要的依赖。那么问题来了,

  • maven是怎么引入依赖的 ?
  • maven引入依赖后是如何管理的 ?

方便参考,我们引入一段pom.xml文件声明依赖的代码示例:

<dependencys>
	<dependency>
      <groupId></groupId>
      <artifactId></artifactId>
      <version></version>
      <type></type>
      <scope></scope>
      <optional></optional>
      <exclusions>
        <exclusion></exclusion>
      </exclusions>
	</dependency>
</dependencys>
  • type | 依赖类型,对应构件中定义的 packaging,可不声明,默认为 jar;
  • scope | 依赖范围
  • optional | 依赖是否可选
  • exclusions | 排除传递依赖

2. 依赖声明

依赖声明三要素坐标 groupIdartifactIdversion 通过坐标可定位依赖的某个版本的jar包,maven会自动从远程的中央仓库下载到我们本地电脑上,在打包的时候,就会自动使用。

3. 依赖范围

<scope></scope>

Maven有三种classspath(项目中依赖的各种类)。简单来说,不同的依赖范围,会导致依赖包在编译测试或者打包运行的时候,有时候可以使用,有时候不能使用对应的依赖。

  • 在编译源代码的时候;

  • 在编译测试代码以及执行测试代码的时候;

  • 实际运行项目的时候;

compile

默认,对编译、测试和运行的classpath都有效。一般都是用这种scope

test

仅对测试代码的时候有效,编译或者运行代码的时候无效。比如junit,一些测试框架,或者只有在测试代码中才会使用的一些依赖,会设置为test,这个的好处在于说,打包的时候这种test scope是不会放到最终的发布包里面,减少发布包的大小。

provided

编译和测试的时候有效,运行的时候无效,因为可能环境已经提供了,比如servlet-api,在运行的时候,servlet容器会提供依赖。servlet-api是用来开发java web项目的,可能你在开发和执行单元测试的时候,需要在pom.xml里面声明这个servlet-api的依赖,因为要写代码和测试代码。但是最终打包之后,放到tomcat容器里面去跑的时候,是不需要将这个servlet-api的依赖包打入发布包中的,因为tomcat容器本身就会给你提供servlet-api的包。

runtime

测试和运行代码的时候有效,编译代码的时候无效,比如jdbc的驱动实现类,比如mysql驱动。因为写代码的时候是基于javax.sql包下的标准接口去写代码的。然后在测试的时候需要用这个包,在实际运行的时候才需要用这个包的,在编译的时候只要javax.sql接口就可以了,不需要mysql驱动类。

当然,一般我们声明mysql驱动的时候,不会设置为runtime,因为也许你在开发代码的时候会用到mysql驱动特定的api接口,不仅仅只是用javax.sql。

总结,如下表所示:

compile test provided runtime
编译
测试
运行

4. 依赖传递

如下表,所示展示传递性依赖机制对依赖范围的影响,第一列:一级依赖 | 第一行:二级依赖

compile test provided runtime
compile compile runtime
test test test
provided provided provided provided
runtime runtime runtime

在早期,我们在做java项目的时候,基本都是纯手动直接放一大堆jar包。然后尝试运行,发现报错,根据报错的提示内容,比如缺失某某类。然后我们再去找对应jar包。继续执行又报错,提示说缺失某某类,每个依赖可能有其他的依赖,而其他依赖又有别的依赖,循环往复,极其麻烦,仅仅配置好jar包运行初期项目就让人心力憔悴了。

maven的出现让我们极大的摆脱了手工干预,让我们能够腾出时间去专心开发。我们说说Maven的传递性依赖,就是说它会自动帮我们递归解析所有的依赖,然后负责将依赖下载下来,接着所有层级的依赖,都会成为我们的项目的依赖,不需要我们手工干预。所有需要的依赖全部下载下来,不管有多少层级。

比如,我们依赖于A,A是compile;A依赖于B,B是test;我们对B的依赖范围是空,就是我们不会去依赖B,因为你自己想想都知道,A只有在测试的时候才会使用B。我们依赖A是生产用的,我们去依赖B干嘛?B是给A测试的。

5. 依赖调解

既然说maven会自动解析所有层级的依赖,给我们自动下载所有的依赖,但是可能会出现依赖冲突的问题,

maven如何解决依赖冲突?

  1. 比如A->B->C->X(1.0),A->D->X(2.0),A有两个传递性依赖X,不同的版本,就产生了依赖冲突的问题。

根据依赖调解机制,我们采用就近原则,离A最近的A就依赖谁(X1.0)。

  1. 再比如,如果A->B->X(1.0)和A->D->X(2.0),路径等长呢?

根据依赖调解机制,我们采用第一声明原则,就是哪个依赖在pom.xml里先声明,就用哪个!

6. 可选依赖

<optional>true</optional>
此时依赖传递失效,不会向上传递

怎么理解?

比如:如果A依赖于B,B依赖于C,B对C的依赖是optional,那么A就不会依赖于C。反之,如果没有optional,根据传递性依赖机制,A会依赖于C。

猜你喜欢

转载自blog.csdn.net/weixin_43980975/article/details/114786684