文章目录
相关文章:
maven pom.xml详解
maven 依赖树(dependency、dependencyManagement)
使用 spring-boot-dependencies 方便管理项目依赖
1. 概述
spring boot工程示例:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--引入spring boot-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<!--定义自己父项目的基本信息-->
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<!--定义子模块-->
<modules>
<module>mall-common</module>
<module>mall-order</module>
</modules>
<!--版本号参数-->
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR8</spring-cloud.version>
</properties>
<!--版本号参数-->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!--定义统一的版本号,但是子模块不会直接继承-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
<version>${spring-cloud.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.1 父pom中的dependencies
dependencies 下引用的maven 依赖,默认被其子模块全部依赖
,其子模块不用显示引用
优点:所有的子模块,不用再次引用,公用父模块的引用和版本号管理
缺点:子模块引用冗余,对于自己不需要引用的包,也会被引用过来
1.2 父pom中的dependencyManagement
父pom通过dependencyManagement管理,引用相关maven依赖后,子模块需要再次引用,但是不需要设置版本号
。版本号由父pom中的dependencyManagement 引用的依赖及版本号统一管理
若子模块中,想单独升级某一个依赖的版本号,则可在子模块中针对某个依赖添加版本号,此时子模块下的那个依赖的版本号会覆盖父pom
对该依赖设置的版本号
优点:父pom 统一管理各个依赖的版本号,子模块可以按需要引用自己需要的依赖,灵活设置自己需要的依赖版本号或直接依赖父pom的版本号
缺点:子模块pom中要显示引用自己需要的依赖
2. 查看依赖树
CMD命令行下,进入pom.xml所在的目录,可以是父模块的目录,也可以是子模块的目录,区别在于前者会按子模块分级,展示所有模块的依赖树,后者只查子模块自身相关的依赖树,后者的结果是前者的一个子集。
- 通过控制台查看:
mvn dependency:tree
- 如果要输出到文件,找到pom文件的位置 进入命令行
mvn dependency:tree >d:/tree.txt
- 只查看包含指定的jar包
includes表示按条件过滤,verbose表示显示详细信息,方便处理冲突
mvn dependency:tree -Dverbose -Dincludes=groupId[:artifactId][:version]
括号中的参数是可选项
mvn dependency:tree -Dverbose -Dincludes=org.springframework:spring-tx
3. 处理冲突
3.1 处理冲突的原则
简介:处理jar包依赖冲突,首先,对于多个jar包都引用同一jar包的情况,最好是在程序中显式定义被共同引用的jar包的依赖,来统一版本号,方便维护
如果A和B都依赖同一jar包C,可能会出现两种情况
1.A和B引用的C版本相同
这时按照pom定义顺序选择第一个即可,没有冲突问题,如果在项目的maven中显示定义了C依赖,那么用选择项目定义的依赖,反正version都一样,没有影响
2.A和B依赖的C版本不同
选择版本高的那个,这时会出现两种结果
(1) 高版本兼容低版本,所以不会出现问题
(2)高版本不兼容低版本,假如A依赖C2版本,B依赖C3版本,C3不兼容C2
,maven选择了高版本C3,对A来说会出现问题
有3种解决方法
[1]提升A版本,找到依赖C3的A版本
[2]如果B版本也可依赖C2,在项目的maven中显示定义对C2的依赖,这样所有都使用C2版本
[3]如果B版本不支持C2版本,只能降低B版本,找到依赖C2的B版本
从功能性和可维护性考虑,
高版本提供的功能更多,bug更少
,优先考虑1,再考虑2,最后考虑3
3.2 如何处理冲突
3.2.1、maven自动解决依赖冲突的规则是什么?
第一原则:路径最近者优先
项目A有如下的依赖关系:
A->B->C->X(1.0)
A->D->X(2.0)
则该例子中,X的版本是2.0
####第二原则:路径相等,先声明者优先
项目A有如下的依赖关系:
A->B->Y(1.0)
A->C->Y(2.0)
若pom文件中B的依赖坐标先于C进行声明,则最终Y的版本为1.0
3.2.2 如何从依赖树中找到自己预期的版本,是被哪个jar给覆盖了?
先来看个最简单的例子
例1:仅涉及单个工程或模块
slf4j-log4j12仅依赖slf4j-api这一唯一的包,前者我们定义1.7.22,但是我们故意把后者单独引入一个1.7.25的高版本
<!--slf4j-log4j12 ,slf4j-api 均是直接引入-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.22</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
下面是依赖树日志:
[INFO] +- org.slf4j:slf4j-log4j12:jar:1.7.22:compile
[INFO] | +- (org.slf4j:slf4j-api:jar:1.7.22:compile - omitted for conflict with 1.7.25)
[INFO] | \- log4j:log4j:jar:1.2.17:compile
[INFO] \- org.slf4j:slf4j-api:jar:1.7.25:compile
-
compile
当前包需要编译入项目包中,和版本冲突无关。与
compile
意思相反的provided
,表示当前包不需要编译入项目包中,也就是说该包将会以其他方式引入,比如tomcat的lib公共库已经提供了此包引用,此处不需以compile形式再次引入。 -
omitted for conflict with 1.7.25
表示默认依赖的是1.7.22,并与其他版本产生了冲突1.7.25(实际使用的版本),丢弃当前1.7.22。
此时会根据前文讲述的冲突规则,最近规则,优先加载1.7.25
omitted for conflict
一般是指存在2个版本有冲突,至少出现一次直接引用(个人总结的,也不一定对,比如如果没有一个直接引入的,但是有2个间接引入的,会有该关键词出现吗?)。这个例子slf4j-log4j12和slf4j-api都是直接引入的。
eclipse插件也能方便的帮助我们查看依赖树,切换pom.xml至Dependency Hierachy标签页
扩展知识,怎么知道slf4j-log4j12会有个默认依赖的slf4j-api?
原因是每个jar自身也是有pom.xml的,我们查看本地仓库的slf4j-log4j12
例如 D:\maven\repo\org\slf4j\slf4j-log4j12\1.7.22\slf4j-log4j12-1.7.22.pom
,里面有如下配置:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<type>test-jar</type>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
现在一目了然,一层层的依赖就是这么来的。
例2:涉及父子模块
在查看依赖树之前我们先来了解一下dependencyManagement
的用法。
在包含父子项目中,允许在顶层的POM文件中,定义dependencyManagement
元素。通过它元素来管理jar包的版本,让子项目中引用一个依赖而不用显示的列出版本号
。Maven会沿着父子层次向上走,直到找到一个拥有dependencyManagement元素的项目,然后它就会使用在这个dependencyManagement元素中指定的版本号。
在父模块中定义了org.slf4j为高版本1.7.25,子模块中定义了slf4j-log4j12为低版本1.7.22
myparent/pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>my</groupId>
<artifactId>myParent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!--父模块类型为pom -->
<packaging>pom</packaging>
<modules>
<!--定义子模块 -->
<module>mychild</module>
</modules>
<dependencyManagement>
<!-- 这样做的好处:统一管理项目的版本号,确保应用的各个项目的依赖和版本一致 -->
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>
</dependencies>
</dependencyManagement>
mychild/pom.xml
<parent>
<groupId>my</groupId>
<artifactId>myParent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>mychild</groupId>
<artifactId>mychild</artifactId>
<dependencies>
<!--直接引入slf4j-log4j12,但是间接引入slf4j-api-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.22</version>
</dependency>
</dependencies>
依赖树
\- org.slf4j:slf4j-log4j12:jar:1.7.22:compile
+- org.slf4j:slf4j-api:jar:1.7.25:compile (version managed from 1.7.22)
\- log4j:log4j:jar:1.2.17:compile
version managed from 1.7.22
表示最终会1.7.25版本,version managed表明最终版本的确认过程涉及<dependencyManagement></dependencyManagement>
中声明的元素。
与例1相比,这里只有slf4j-log4j12作直接引入,slf4j-api是间接引入。
dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。如果不在子项目中声明依赖,是不会从父项目中继承下来的;只有在子项目中写了该依赖项,并且没有指定具体版本,才会从父项目中继承该项,并且version和scope都读取自父pom。
例3
本例基于例2,注意把slf4j-api也改为直接引入
mychild/pom.xml
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.22</version>
</dependency>
<!--与例2 相比,新增了直接引入slf4j-api-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.22</version>
</dependency>
</dependencies>
[INFO] +- org.slf4j:slf4j-log4j12:jar:1.7.22:compile
[INFO] | +- (org.slf4j:slf4j-api:jar:1.7.25:compile - version managed from 1.7.22; omitted for conflict with 1.7.22)
[INFO] | \- log4j:log4j:jar:1.2.17:compile
[INFO] \- org.slf4j:slf4j-api:jar:1.7.22:compile
version managed from 1.7.22; omitted for conflict with 1.7.22
表示使用1.7.25,不用1.7.22,version managed表明最终版本的确认过程涉及<dependencyManagement></dependencyManagement>
中声明的元素;
omitted for conflict表示直接引入的元素有冲突(个人总结的,也不一定对,比如如果没有一个直接引入的,但是有2个间接引入的,会有该关键词出现吗?)
例4
本例基于例2,额外间接引用slf4j-api,总共有2次额外引用
mychild/pom.xml
<!-- 间接引用slf4j-api -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.22</version>
</dependency>
<!-- 新增间接引用slf4j-api -->
<dependency>
<artifactId>kafka-clients</artifactId>
<groupId>org.apache.kafka</groupId>
<version>2.1.1</version>
</dependency>
依赖树:
+- org.slf4j:slf4j-log4j12:jar:1.7.22:compile
| +- org.slf4j:slf4j-api:jar:1.7.25:compile (version managed from 1.7.22)
| \- log4j:log4j:jar:1.2.17:compile
\- org.apache.kafka:kafka-clients:jar:2.1.1:compile
+- com.github.luben:zstd-jni:jar:1.3.7-1:compile
+- org.lz4:lz4-java:jar:1.5.0:compile
+- org.xerial.snappy:snappy-java:jar:1.1.7.2:compile
\- (org.slf4j:slf4j-api:jar:1.7.25:compile - version managed from 1.7.22; omitted for duplicate)
version managed from 1.7.22;omitted for duplicate
使用1.7.25
omitted for duplicate 表明 存在重复的引用
参考文章《maven冲突》