医药加盟网站模板,西安维护网站,理财网站免费建设,做视频网站用网站空间还是服务器准备工作
1、创建一个空工程 maven_dependency_conflict_demo#xff0c;在 maven_dependency_conflict_demo 创建不同的 Maven 工程模块#xff0c;用于演示本文的一些点。
什么是依赖冲突#xff1f;
当引入同一个依赖的多个不同版本时#xff0c;就会发生依赖冲突。…准备工作
1、创建一个空工程 maven_dependency_conflict_demo在 maven_dependency_conflict_demo 创建不同的 Maven 工程模块用于演示本文的一些点。
什么是依赖冲突
当引入同一个依赖的多个不同版本时就会发生依赖冲突。
演示 1
步骤
1、在 maven_dependency_conflict_demo 中创建 2 个基于 Maven 的 JavaSE 项目 maven_00 和 maven_01。 2、在 maven_01 的 pom.xml 文件中引入 junit 4.12 版本。 3、将 maven_01 打包安装到私服。 4、在 maven_00 的 pom.xml 文件中引入 junit 4.13 版本和 maven_01。
结果如下图所示可以看到的的确确发生了依赖冲突而且 Maven 自己处理了依赖冲突。Maven 处理冲突依据的是短路径优先原则后面会提到。
Maven的依赖调解原则
Maven 在遇到依赖冲突时会先根据路径最短优先和声明顺序优先两大原则处理冲突。
1、短路径优先
当一个间接依赖存在多条引入路径时引入路径短的会被解析使用。 见演示 1依赖关系图如下图所示 附代码分支 2411291-larryla-maven-name-first。 2、声明顺序优先
如果存在短路径则优先选择短路径。如果路径相同则先声明者优先。
演示 2
步骤
1、在 maven_dependency_conflict_demo 中创建 3 个基于 Maven 的 JavaSE 项目 maven_02、maven_03 和 maven_04。 2、在 maven_03 中引入 junit 4.12 版本。 3、将 maven_03 打包安装到私服。 4、在 maven_04 中引入 junit 4.13 版本。 5、将 maven_04 打包安装到私服。 6、在 maven_02 中引入 maven_03 和 maven_04查看 junit 版本引入情况。
上面步骤对应的依赖关系图如下 结果如下
(1) 若 maven_03 在 maven_04 前面则 maven_02 中引入的是 junit 4.12 (2) 若 maven_04 在 maven_03 前面则 maven_02 中引入的是 junit 4.13 附代码分支 2411291-larryla-maven-name-first。 3、特殊情况
(1) 子 pom 覆盖父 pom。
如果在父工程和子工程引入了相同依赖的不同版本那么子工程中的版本会覆盖覆盖父工程中的版本。
步骤
1、在 maven_dependency_conflict_demo 中创建 2 个基于 Maven 的 JavaSE 项目 maven_07 和 maven_08。 2、在 maven_07 中引入 junit 4.13 版本。 3、在 maven_08 重新指定的 junit 为 4.12 版本。
结果如下图所示可以看到子工程引入的是 junit 4.12 版本而不是继承自父工程的 4.13 版本这说明子工程覆盖了父工程的版本。 (2) 同一个 pom.xml 文件里配置相同资源的不同版本后配置的覆盖先配置的。
在 maven_07 的 pom.xml 文件中先后引入 MyBatis 3.5.11 和 3.5.7 版本结果如下图。可以看到后声明的 3.5.7 版本覆盖了先声明的 3.5.11 版本。
排除依赖
情景
如果仅仅依靠 Maven 来进行依赖调解在很多情况下是不适用的这时需要我们手动排除依赖。
举个例子当前项目存在下面的依赖关系
依赖链路一A - B - C - X(2.0.0) // dist 3
依赖链路二A - D - X(1.0.0) // dist 2根据路径最短优先原则X(1.0) 会被解析使用也就是说 A 中实际用的是 1.0 版本的 X。但是这样可能会导致错误。
假如 X(2.0) 相较于 X(1.0) 新增了 1 个类和 1 个方法并且 A 中使用了新增的类和方法编译不通过。
问题演示
步骤
1、在 maven_dependency_conflict_demo 中创建 5 个基于 Maven 的 JavaSE 项目 maven_a、maven_b、maven_c 、maven_d 和 maven_x。 2、在 maven_x 的 1.0 版本打包安装到私服的 Release 仓库。 3、将 maven_x 的 2.0 版本打包安装到私服的 Release 仓库。 4、在 maven_c 中引入 maven_x 的 2.0 版本将 maven_c 打包安装到私服的 Release 仓库。在 maven_b 中引入 maven_c将 maven_b 打包安装到私服的 Release 仓库。在 maven_a 中引入 maven_b。 5、在 maven_d 中引入 maven_x 的 1.0 版本将 maven_d 打包安装到私服的 Release 仓库。在 maven_a 中引入 maven_d。
上面步骤对应的依赖关系图如下 maven_x 1.0.0 版本 在 2.0.0 版本中变化如下
在 BallSports 类中新增方法 playFootBall。新增类 SwimmingSports类中包含方法 FreeStyle。
结果如下图依据短路径优先原则在 maven_a 模块中引入了 maven_x 1.0.0 版本舍弃了 maven_x 2.0.0 版本所以无法使用 2.0.0 版本中新增的类和方法。
解决方案
一般在解决依赖冲突的时候我们都会优先保留版本较高的这是因为大部分 jar 在升级的时候都会做到向下兼容。
1、exclusions标签
具体做法是在 maven_a 的 pom.xml 文件中使用 exclusion 标签显式排除掉 maven_x 1.0.0 版本的依赖。结果如下图所示成功引入 maven_x 2.0.0 版本舍弃 1.0.0 版本。
2、optional标签
我们还可以使用 optional 标签对外隐藏当前所依赖的资源。
具体做法是在 maven_d 中引入 maven_x 1.0.0 版本时指定 optional 标签值为 true。
结果如下图所示可以看到使用 optional 标签可以使 maven_x 1.0.0 版本的 jar 包不再通过 maven_d 传递也就是 maven_a 在引入 maven_b 时得不到 maven_x 1.0.0 的 jar 包。 附代码分支 2411292-larryla-maven-dependency-exclusion。 对比标签和标签的区别
由上可知在 maven_a → maven_d → maven_x 这条分支上我们有两种解决依赖冲突的方法即 exclusions 标签和 optional 标签。两种方法的本质都是不将 maven_x 1.0.0 jar 包传递到 maven_a 中。但区别在于
排除依赖是在 maven_a 引入 maven_d 依赖时同时设置exclusionsmaven_a 知道 maven_x 1.0.0 的存在主动将其排除掉。这种适用于不能修改 maven_d 的配置文件的情况。可选依赖是在 maven_d 上为其依赖项 maven_x 设置optional是 maven_d 不让外界知道依赖 maven_x 的存在不再传递依赖 maven_x此时maven_a 不知道 maven_x 1.0.0 的存在。这种适用于可以修改 maven_d 的配置文件的情况。
思考 1
在问题演示中我们复现了短路径优先可能导致的问题即 maven_a 会引入 maven_x 1.0.0 版本舍弃 maven_x 2.0.0 版本。那么问题来了既然引入了 maven_x 1.0.0 版本那么 maven_b 和 maven_c 会用哪个版本的 maven_x 呢 结果如下由编辑区的方法调用和右侧的依赖管理可以得知maven_a 为了解决依赖冲突选择引入 maven_x 1.0.0 版本而 maven_b 和 maven_c 不受 maven_a 的影响引入的依旧是 maven_x 2.0.0 版本 因此上面给出的依赖传递图不是很精确的应该如下
maven_a 引入 maven_x 1.0.0maven_b、maven_c 引入 maven_x 2.0.0 附代码分支 2411292-larryla-maven-dependency-exclusion。 思考 2
对于情景一节Maven 在依据短路径优先原则调解冲突时可能会带来一些问题。假如 X(2.0) 相较于 X(1.0) 新增了 1 个类和 1 个方法
A 中使用了新增的类编译不通过。A 中使用了新增的方法编译不通过。
我们在问题演示一节复现了上面两个问题并且在解决方案一节中使用两种方案 exclusions 和 optional引入了高版本 maven_x 2.0.0 解决了这个问题。
但是引入高版本就没有问题了吗
还是下图的依赖关系假如 maven_a 引入了 maven_x 2.0.0 版本。 假如 maven_x 2.0.0 相比于 maven_x 1.0.0 版本删掉了一个类但 maven_a 已经引用了这时会报错maven_x 2.0.0 相比于 maven_x 1.0.0 版本删掉了一个方法但 maven_a 已经引用了同样也会报错。
演示
我们重新组织 maven_x 1.0.0 和 maven_x 2.0.0 版本包括的内容。
maven_x 1.0.0 版本内容如下 BallSportsplayBasketBall 方法、playFootBall 方法 SwimmingSportsFreeStyle 方法 maven_x 1.0.0 版本内容变化如下 删掉 BallSports 类中的 playBasketBall 方法 删掉 SwimmingSports 类
步骤
1、在 maven_x 的 1.0 版本打包安装到私服的 Release 仓库。 2、将 maven_x 的 2.0 版本打包安装到私服的 Release 仓库。 3、在 maven_c 中引入 maven_x 的 2.0 版本将 maven_c 打包安装到私服的 Release 仓库。在 maven_b 中引入 maven_c将 maven_b 打包安装到私服的 Release 仓库。在 maven_a 中引入 maven_b。 4、在 maven_d 中引入 maven_x 的 1.0 版本将 maven_d 打包安装到私服的 Release 仓库。在 maven_a 中引入 maven_d。
结果如下可以看到 maven_a 中引入的是 maven_x 2.0.0 版本无法使用 2.0.0 版本相比 1.0.0 版本删掉的方法和类直接编译不通过。 附代码分支 2411301-larryla-maven-dependency-exclusion-high-ver-problem。 聚合工程统一管理版本
聚合工程一个项目允许创建多个子模块多个子模块组成一个整体可以统一进行项目的构建。要弄明白聚合工程得先清楚父子工程的概念
父工程没有任何代码、仅有pom.xml的空项目用来定义公共依赖、插件和配置子工程编写具体代码的子项目可以继承父工程的依赖项、配置还可以独立拓展。
而 Maven 聚合工程就是基于父子工程结构将一个完整项目划分出不同的层次。这种方式可以很好的管理多模块之间的依赖关系以及构建顺序大大提高了开发效率、维护性。
为了防止不同子工程引入不同版本的依赖在父工程中统一对依赖的版本进行控制规定所有子工程都使用同一版本的依赖可以使用dependencyManagement标签来管理。
子工程在使用dependencyManagement中已有的依赖项时不需要写version版本号版本号在父工程中统一管理这样做的好处在于以后为项目的技术栈升级版本时不需要单独修改每个子工程的POM只需要修改父POM文件即可减少了版本冲突的可能性。
演示
步骤
1、在 maven_dependency_conflict_demo 中创建 2 个基于 Maven 的 JavaSE 项目 maven_05 和 maven_06。 2、在 maven_05 中引入 junit 4.13 版本使用 dependencyManagement 标签统一管理。 3、在 maven_06 继承 maven_05 的 junit 4.13 版本。
上面步骤对应的依赖关系图如下 结果如下图所示可以看到在 maven_06 工程中只需要指定 groupId 和 artifactId不需要指定 version就可以从父工程 maven_05 中继承 junit 4.13 依赖。
其他
1、子工程继承父工程的依赖时可以指定版本这时子工程的版本会覆盖父工程的版本。
演示
还是使用演示一节的模块。在 maven_06 模块中指定 junit 的版本为 4.12那么 maven_06 引入的 junit 将是 4.12 版本而不是父工程的 4.13 版本 虽然可以在子工程中指定其他版本但不建议这么做。如果这样做的话使用 dependencyManagement 的意义就不大了。
2、父工程可以使用 properties 标签指定依赖的版本然后在 GAV 坐标中引入该版本。这样在升级依赖版本时只需要修改 properties 标签中变量的值而无需修改 GAV 中的 version 标签的值。
演示
还是使用演示一节的模块。在 maven_05 的 pom.xml 文件中在 properties 标签中自定义一个 junit-version 标签用于指定 junit 依赖的版本号。然后在 version 标签中使用 ${junit-version} 的形式声明版本号。如下图所示maven_06 依旧成功引入了 junit 4.13 版本。 3、dependencyManagement 标签只是用于声明依赖不会在项目中引入依赖。
如下图所示只是在聚合工程的子工程中引入了 junit 依赖父工程并未引入依赖。 4、dependencyManagement 和 dependencies 的区别
dependencies定义强制性依赖写在该标签里的依赖项子工程必须继承。dependencyManagement定义可选性依赖该标签里的依赖项子工程可选择性地继承。 附代码分支 2411302-larryla-maven-dependency-uni-manage 排查依赖冲突
在 MySQL 中我们会借助一些工具进行慢 SQL 分析。那么在 Maven 中我们是否可以借助一些工具进行依赖冲突定位呢
我们可以在插件市场下载Maven Helper 插件辅助排查依赖冲突。 在排除依赖-情景一节中我们分析了如下依赖冲突情况得到的结论是 Maven 会依据短路径优先原则引入 X(1.0) 版本。
依赖链路一A - B - C - X(2.0.0) // dist 3
依赖链路二A - D - X(1.0.0) // dist 2接下来我们使用 Maven Helper 插件来验证一下。
打开 maven_a 的 pom.xml 文件在下方点击 Dependency Analyzer然后点击 Refresh UI会弹出如下界面 提示有一个依赖冲突即 maven_x: 1.0.0。我们点开看看发现与之冲突的是 maven_x 2.0.0。这就和上面呼应上了Maven 依据短路径优先原则选择了 1.0.0 版本舍弃了 2.0.0 版本。 在排除依赖-解决方案一节中我们基于排除依赖引入了 2.0.0 版本这里我们同样保留 2.0.0 版本。在 1.0.0 上右键选择 exclude表示排除掉该版本。然后点击上面的 Reimport再点击 Refresh UI。 返回到 pom.xml 文件发现点击上面的 exclude 按钮就是在 pom.xml 文件中使用了 exclusions 标签
思维导图 代码仓库gitgithub.com:Acura-bit/maven_dependency_conflict_demo.git