Maven依赖机制
依赖传递
依赖相关命令
mvn dependency:list:查看当前项目所有依赖。
mvn dependency:tree:以树的形式显示当前项目的所有依赖,相比mvn dependency:list 列表显示,能很清楚的看到某个依赖是通过哪条依赖路径引入的。
mvn dependency:analyze:分析项目的依赖关系,并确定哪些依赖是:使用和声明、使用和未声明、未使用和声明。
依赖的传递性
如有依赖关系为a->b->c,a依赖b,称为直接依赖。a本身不依赖c,但c通过b传递给a,称c为a的传递性依赖。
通过mvn dependency:list查看a项目的依赖列表,可以看到依赖b和c:
[info] --- maven-dependency-plugin:2.8:list (default-cli) @ a --- [info] [info] the following files have been resolved: [info] com.nocoffee:b:jar:0.0.1-snapshot:compile [info] junit:junit:jar:3.8.1:test [info] com.nocoffee:c:jar:0.0.1-snapshot:compile [info] [info] ------------------------------------------------------------------------ [info] build success [info] ------------------------------------------------------------------------
依赖调解
场景1:
路径1:a->b->c(version:1.0)
路径2:a->d->e->c(version:2.0)
通过两条依赖路径可以看出,a的传递性依赖的c有两个不同版本,为了避免依赖重复,最终只能选择一个。这种情况maven采用路径最近者优先的原则来处理,路径1中c到a的距离比路径2中c到a的距离要短,于是路径1中c(version:1.0)最终被a依赖。
mvn dependency:tree 查看依赖路径:
[info] --- maven-dependency-plugin:2.8:tree (default-cli) @ a --- [info] com.nocoffee:a:jar:0.0.1-snapshot [info] +- com.nocoffee:b:jar:0.0.1-snapshot:compile [info] | \- com.nocoffee:c:jar:0.0.1-snapshot:compile [info] | \- com.nocoffee:d:jar:0.0.1-snapshot:compile [info] | \- com.nocoffee:e:jar:0.0.1-snapshot:compile [info] \- junit:junit:jar:3.8.1:test [info] ------------------------------------------------------------------------ [info] build success [info] ------------------------------------------------------------------------
场景2:
路径1:a->b->c(version:1.0)
路径2:a->d->c(version:2.0)
路径1和路径2中c到a的距离是相同的,通过路径最近者优先原则无法判断该使用哪个依赖,此时maven会使用第一声明者优先原则进行选择,第一声明者优先原则是指在pom依赖中声明顺序最靠前的那个依赖会被选择。在a的pom文件中b的声明靠前,于是c(version:1.0)会被选择。
<!-- a的pom.xml中依赖部分--> <dependencies> <dependency> <groupid>com.nocoffee</groupid> <artifactid>b</artifactid> <version>0.0.1-snapshot</version> </dependency> <dependency> <groupid>com.nocoffee</groupid> <artifactid>d</artifactid> <version>0.0.1-snapshot</version> </dependency> <dependency> <groupid>junit</groupid> <artifactid>junit</artifactid> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies>
mvn dependency:tree 查看依赖路径:
[info] --- maven-dependency-plugin:2.8:tree (default-cli) @ a --- [info] com.nocoffee:a:jar:0.0.1-snapshot [info] +- com.nocoffee:b:jar:0.0.1-snapshot:compile [info] | \- com.nocoffee:c:jar:0.0.1-snapshot:compile [info] +- com.nocoffee:d:jar:0.0.1-snapshot:compile [info] \- junit:junit:jar:3.8.1:test [info] ------------------------------------------------------------------------ [info] build success [info] ------------------------------------------------------------------------
排除依赖
在场景2中,如果要使a依赖c(version:2.0) ,则可以配置排除依赖:
<dependencies> <dependency> <groupid>com.nocoffee</groupid> <artifactid>b</artifactid> <version>0.0.1-snapshot</version> <exclusions> <!-- 排除依赖 c --> <exclusion> <groupid>com.nocoffee</groupid> <artifactid>c</artifactid> </exclusion> </exclusions> </dependency> <dependency> <groupid>com.nocoffee</groupid> <artifactid>d</artifactid> <version>0.0.1-snapshot</version> </dependency> <dependency> <groupid>junit</groupid> <artifactid>junit</artifactid> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies>
mvn dependency:tree 查看依赖路径,a不再通过b依赖c,而是通过d依赖c:
[info] --- maven-dependency-plugin:2.8:tree (default-cli) @ a --- [info] com.nocoffee:a:jar:0.0.1-snapshot [info] +- com.nocoffee:b:jar:0.0.1-snapshot:compile [info] +- com.nocoffee:d:jar:0.0.1-snapshot:compile [info] | \- com.nocoffee:c:jar:0.0.1-snapshot:compile [info] \- junit:junit:jar:3.8.1:test [info] ------------------------------------------------------------------------ [info] build success [info] ------------------------------------------------------------------------
可选依赖
可以将某个依赖配置为可选依赖,则该依赖不会参与依赖传递。
以场景2为例,可以在b的pom.xml里将c配置为可选依赖,使a依赖d的c(version:2.0)。
<!-- b的pom.xml --> <dependency> <groupid>com.nocoffee</groupid> <artifactid>c</artifactid> <version>0.0.1-snapshot</version> <!-- 设置可选依赖 --> <optional>true</optional> </dependency>
mvn dependency:tree 查看依赖路径,a不再通过b依赖c,而是通过d依赖c:
[info] --- maven-dependency-plugin:2.8:tree (default-cli) @ a --- [info] com.nocoffee:a:jar:0.0.1-snapshot [info] +- com.nocoffee:b:jar:0.0.1-snapshot:compile [info] +- com.nocoffee:d:jar:0.0.1-snapshot:compile [info] | \- com.nocoffee:c:jar:0.0.1-snapshot:compile [info] \- junit:junit:jar:3.8.1:test [info] ------------------------------------------------------------------------ [info] build success [info] ------------------------------------------------------------------------
依赖范围
maven在编译、测试、运行时都会使用不同的classpath,依赖范围是用来控制依赖和三种classpath的关系。
依赖范围介绍
- compile:编译依赖范围,默认使用该依赖范围,在所有classpath中都可用,并且依赖项将传播到依赖项目。
- provided:已提供依赖范围,只对于编译和测试classpath有效,运行时无效,如servlet api,此范围不具有传递性。
- runtime:运行时依赖范围,只对于测试和运行classpath有效,但在编译主代码时无效。
- test:测试依赖范围,只对于测试的classpath有效,仅适用于测试编译和执行阶段,如junit。此范围不具有传递性。
- system:系统依赖范围,该依赖于三种classpath的关系和provided依赖范围完全一致。区别在于system依赖范围必须通过systempath元素显示的指定依赖文件的路径。
-
import:导入依赖范围,该依赖范围不会对三种classpath产生影响,只有在
部分中的pom类型依赖项才支持此范围,它指示要替换为指定pom的 部分中的有效依赖项列表的依赖项。由于它们被替换,具有导入范围的依赖项实际上不参与限制依赖项的传递性。
依赖范围对依赖传递的影响
每个范围(import导入依赖范围除外)以不同方式影响传递依赖性,如下表所示。以a->b->c依赖路径为例,左边第一列为第一直接依赖(b在a中的依赖范围),最上面一行为第二直接依赖(c在b中的依赖范围),交叉单元格为传递性依赖范围(c在a中的依赖范围)。
compile | provided | runtime | test | |
compile | compile(*) | - | runtime | - |
provided | provided | - | provided | - |
runtime | runtime | - | runtime | - |
test | test | - | test | - |
推荐阅读
-
在net Core3.1上基于winform实现依赖注入实例
-
rabbitmq~消息失败后重试达到 TTL放到死信队列(事务型消息补偿机制)
-
Maven设置本地仓和阿里云远程仓
-
Qt事件分发机制源码分析之QApplication对象构建过程
-
Maven入门【小白千万别点进】
-
Jenkins + Docker + dockerfile-maven-plugin + Harbor CI/CD spring-boot项目的最轻量级配置
-
Android签名机制
-
面试必问:Spring循环依赖的三种方式
-
Ioc依赖注入:Unity4.0.1 在项目中的应用 (MVC和API)
-
maven项目集成Quartz定时任务框架,实现批处理功能