Maven依赖管理总结

深藏阁楼爱情的钟 2022-05-15 13:56 386阅读 0赞

前言

Maven作为当下最流行的项目管理工具,其中核心功能就是依赖管理。本文主要总结Maven依赖管理中依赖范围和依赖冲突的解决。

依赖范围

依赖是maven项目引用的资源架包,依赖范围就是这些资源架包在maven项目中的作用范围,反过来说,maven项目通过依赖范围来控制何时引用资源架包。之前有介绍maven的默认生命周期,(compile,test,package,install,deploy)。maven的依赖范围用scope关键字表示,恰好也是五种,虽然不是和生命周期完整对应的,但是基于生命周期划分的。
在这里插入图片描述
通过下表可以知道依赖范围表示的作用域。
















































依赖范围 对于编译执行环境有效 对于测试执行环境有效 对于运行时执行环境有效 例 子
compile spring-core
test × × junit
provided × servlet-api
runtime × JDBC驱动
system × 本地的,Maven仓库之外的类库

compile:依赖范围默认值是compile,这是最常用的,如果maven项目中引入一个依赖,没有添加scope依赖范围,那么默认的就是compile,表示maven项目在编译,测试,运行阶段都需要引用该依赖。
test:表示该依赖包只和测试相关,测试代码的编译和运行会引用该依赖。
provided:表示maven项目只在编译和测试时引用该依赖,如果将项目打包运行时,则不会引入该依赖,如servlet-api,这是web项目常用的架包,在项目编译和测试时都需要用到该架包,如果项目需要运行,则需要将项目部署到tomcat或其他web服务器上,但是tomcat中自带了servlet-api,如果maven项目中引入了servlet-api,那么会和tomcat中的servlet-api产生冲突,所以可以使用provided限定servlet-api,让maven项目在打包时不再引入servlet-api.

runtime和system是不常用的。runtime表示在测试和运行阶段需要引入该依赖,在编译阶段不引入该依赖,如JDBC的驱动包,因为JDBC驱动是通过反射机制加载的,所以不参与项目编译过程。system的作用域和provided类似,表示引用仓库之外的依赖,需要通过systemPath指定本地依赖的路径,除了特殊情况基本不使用。

传递依赖冲突的解决

先了解一下传递依赖以及什么情况下会产生冲突。
传递依赖,如果项目A需要引入依赖包B,依赖包B又引入了依赖包C,那么B是A的直接依赖,C是A的传递依赖。如果项目A还需要引入依赖包D,依赖包D也引用了依赖包C,当依赖包B引用的C和依赖包D引用的C版本不同时,则发生了依赖冲突。
通过实例说明:一下是Spring的架构图(4.X版本的官方参考文档,5.X版本的没找到,地址:https://docs.spring.io/spring/docs/4.3.21.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#overview-modules)
在这里插入图片描述

现在项目中使用到AOP和Messaging两个模块,在pom.xml文件中引入两个模块的架包坐标信息。

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-aop</artifactId>
  4. <version>4.3.7.RELEASE</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework</groupId>
  8. <artifactId>spring-messaging</artifactId>
  9. <version>4.1.7.RELEASE</version>
  10. </dependency>

由下面的依赖层次结构图可知这两个模块都依赖于spring-core和spring-beans核心架包,这里只看spring-beans架包。
在这里插入图片描述
由上图可知,项目(A)依赖spring-aop(B)和spring-messaging(D)两个架包,而spring-aop(B)又依赖于spring-beans ( C),spring-messaging(D)也依赖于spring-banes( C),但是两个依赖的C版本不一样,spring-messaging引入的spring-beans:4.1.7.RELEASE后面括号中提示因为和4.3.7版本冲突而忽略了,也就是说spring-beans:4.1.7并没有被项目(A)引用。
在这里插入图片描述
查看此时项目中引用的maven依赖,spring-beans的版本是4.3.7.
以上情况是因为maven具有自调节的maven传递依赖的冲突解决。
总结一下maven传递依赖冲突解决方案:

1.Maven自调节原则

1.1 第一声明优先原则

在pom.xml文件中引入两个模块 AOP 和 Messaging 的架包坐标信息,先声明的是AOP模块,当AOP和Messaging的依赖包发生冲突时,项目只会引入AOP模块底下的冲突依赖包,Messaging的冲突依赖包则会被排除。
将AOP和Messaging的声明信息调换一下位置:

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-messaging</artifactId>
  4. <version>4.1.7.RELEASE</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework</groupId>
  8. <artifactId>spring-aop</artifactId>
  9. <version>4.3.7.RELEASE</version>
  10. </dependency>

在pom.xml文件中声明Messaging模块,或者先加入spring-messaging架包,那么项目中会引入spring-messaging下的冲突架包。
在这里插入图片描述

1.2 路径近者优先原则

这个就比较好理解了,就是直接依赖优先于传递依赖,当然会存在多层依赖,当多层依赖发生冲突时,maven的依赖管理会优先引入依赖层级最少的冲突依赖。如:
在这里插入图片描述
项目直接引入spring-aop(4.3.7版本),项目引入的spring-messaging架包的依赖 spring-context,spring-context又依赖于spring-aop(4.1.7版本),那么项目肯定会优先引入AOP的4.3.7版本,因为这个依赖路径最短。

因为Maven具有自调节依赖冲突解决原则,所以项目中不会发生依赖冲突的错误,但是如果想要自定义选择依赖包的版本,可以用以下方法。

2.排除依赖

排除依赖顾名思义就是将不需要的依赖排除,这也是依赖冲突的一种解决方案。

如,项目A依赖于B,B依赖于C,项目A依赖于D,D依赖于C,如果在项目A的pom.xml中先定义B,根据maven自调节的第一声明优先原则,那么D依赖的C就会被默认排除。此时,如果使用关键字exclusion在B中排除C,那么C自然不会再发生冲突了,因为B依赖的C被排除了,项目A会引入D依赖的C。

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-messaging</artifactId>
  4. <version>4.1.7.RELEASE</version>
  5. <exclusions>
  6. <exclusion>
  7. <groupId>org.springframework</groupId>
  8. <artifactId>spring-beans</artifactId>
  9. </exclusion>
  10. </exclusions>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework</groupId>
  14. <artifactId>spring-aop</artifactId>
  15. <version>4.3.7.RELEASE</version>
  16. </dependency>

在这里插入图片描述
spring-messaging中使用exclusion关键字排除spring-beans依赖包,可以看到依赖层级中,spring-messaging依赖中没有spring-beans,所以项目中引入的是spring-aop底下的spring-beans(4.3.7版本)。这就是排除依赖的使用。

3.版本锁定(重点)

所谓版本锁定就是指定依赖包的版本,这种依赖冲突的解决方案是目前实际项目中使用的最多的。
版本锁定使用< dependencyManagement >节点

  1. <dependencyManagement>
  2. <dependencies>
  3. <dependency>
  4. <groupId>org.springframework</groupId>
  5. <artifactId>spring-core</artifactId>
  6. <version>4.3.2.RELEASE</version>
  7. </dependency>
  8. <dependency>
  9. <groupId>org.springframework</groupId>
  10. <artifactId>spring-aop</artifactId>
  11. <version>4.3.2.RELEASE</version>
  12. </dependency>
  13. <dependency>
  14. <groupId>org.springframework</groupId>
  15. <artifactId>spring-context</artifactId>
  16. <version>4.3.2.RELEASE</version>
  17. </dependency>
  18. <dependency>
  19. <groupId>org.springframework</groupId>
  20. <artifactId>spring-messaging</artifactId>
  21. <version>4.3.2.RELEASE</version>
  22. </dependency>
  23. <dependency>
  24. <groupId>org.springframework</groupId>
  25. <artifactId>spring-beans</artifactId>
  26. <version>4.3.2.RELEASE</version>
  27. </dependency>
  28. <dependency>
  29. <groupId>org.springframework</groupId>
  30. <artifactId>spring-expression</artifactId>
  31. <version>4.3.2.RELEASE</version>
  32. </dependency>
  33. </dependencies>
  34. </dependencyManagement>
  35. <dependencies>
  36. <dependency>
  37. <groupId>org.springframework</groupId>
  38. <artifactId>spring-messaging</artifactId>
  39. <version>4.1.7.RELEASE</version>
  40. </dependency>
  41. <dependency>
  42. <groupId>org.springframework</groupId>
  43. <artifactId>spring-aop</artifactId>
  44. <version>4.3.7.RELEASE</version>
  45. </dependency>
  46. </dependencies>

需要说明的是< dependencyManagement >节点仅仅起指定依赖包版本的作用。依赖包的引入仍然是由以下内容确定,如果pom.xml引入依赖包的节点设置了< version >,那么项目中则会引入该版本,如果在< dependencyManagement >节点指定了依赖包的版本,引入依赖包的节点可以不用设置版本。

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-messaging</artifactId>
  5. <version>4.1.7.RELEASE</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework</groupId>
  9. <artifactId>spring-aop</artifactId>
  10. <version>4.3.7.RELEASE</version>
  11. </dependency>
  12. </dependencies>

如下图所示 :spring-messaging和spring-aop版本分别是4.1.7和4.3.7,而其他的版本都指定为4.3.2版本。如果将依赖包的坐标信息中的< version >去掉,那么spring-messaging和spring-aop的版本也会变为< dependencyManagement >设定的4.3.2版本。
在这里插入图片描述
为了项目版本依赖管理的统一和方便升级,版本信息通常使用OGNL表达式表示,如下:

  1. <properties>
  2. <spring-version>4.3.2.RELEASE</spring-version>
  3. </properties>
  4. <dependencyManagement>
  5. <dependencies>
  6. <dependency>
  7. <groupId>org.springframework</groupId>
  8. <artifactId>spring-core</artifactId>
  9. <version>${spring-version}</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.springframework</groupId>
  13. <artifactId>spring-aop</artifactId>
  14. <version>${spring-version}</version>
  15. </dependency>
  16. <dependency>
  17. <groupId>org.springframework</groupId>
  18. <artifactId>spring-context</artifactId>
  19. <version>${spring-version}</version>
  20. </dependency>
  21. <dependency>
  22. <groupId>org.springframework</groupId>
  23. <artifactId>spring-messaging</artifactId>
  24. <version>${spring-version}</version>
  25. </dependency>
  26. <dependency>
  27. <groupId>org.springframework</groupId>
  28. <artifactId>spring-beans</artifactId>
  29. <version>${spring-version}</version>
  30. </dependency>
  31. <dependency>
  32. <groupId>org.springframework</groupId>
  33. <artifactId>spring-expression</artifactId>
  34. <version>${spring-version}</version>
  35. </dependency>
  36. </dependencies>
  37. </dependencyManagement>
  38. <dependencies>
  39. <dependency>
  40. <groupId>org.springframework</groupId>
  41. <artifactId>spring-messaging</artifactId>
  42. </dependency>
  43. <dependency>
  44. <groupId>org.springframework</groupId>
  45. <artifactId>spring-aop</artifactId>
  46. </dependency>
  47. </dependencies>

spring-version是一个自定义的常量名,可以设定值为依赖包的版本,然后在
< dependencyManagement >中引用该值,如果项目需要升级依赖包的版本,只需要改变该常量值即可。

传递依赖范围

如A依赖于B,B依赖于C,A传递依赖于C,依赖范围关系如下:
在这里插入图片描述
之后会总结Maven项目拆分的相关知识,再说明对于传递依赖范围的理解。

发表评论

表情:
评论列表 (有 0 条评论,386人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Maven依赖管理

    定义maven坐标 每个maven工程都需要定义本工程的坐标,坐标是maven对jar包的身份定义。 <!--项目名称,定义为组织名+项目名,类似包名-->

    相关 maven依赖机制及依赖管理

    maven依赖机制及依赖管理 依赖性传递: 依赖调解: 当项目中出现多个版本构件依赖的情形,依赖调解决定最终应该使用哪个版本。当然,你也可以在项目POM文件中

    相关 Maven依赖管理大全

    大家都知道随着业务的进展,项目会变得越来越多,这个时候如果没有一个统一的依赖管理中心,就会有很多问题发生。 如果没有依赖管理中心,会发生哪些问题呢? 1. 项目的依赖会有

    相关 Maven 依赖管理

    Maven 依赖管理 Maven 一个核心的特性就是依赖管理。当我们处理多模块的项目(包含成百上千个模块或者子项目),模块间的依赖关系就变得非常复杂,管理也变得很困难。针...