Page 147 - 《软件学报》2025年第7期
P. 147
3068 软件学报 2025 年第 36 卷第 7 期
的冲突, 而不是模块与依赖库之间的冲突, 因此不会出现因为冲突类在模块中而被忽略的情况. 在这种情况下,
Maven 和 Gradle 项目都倾向于选择依赖树前序遍历序列中靠前的依赖库中的类.
(a) 类型 1.1 依赖于
模块 mod 依赖库 lib
包含类 包含类
mod.Dup mod.Dup
public class Dup{ public class Dup{
… … 模块和依赖库之间包含冲突类
} } 冲突
依赖于 依赖于
模块 mod 依赖库 lib1 依赖库lib2
包含类 包含类
lib.Dup lib.Dup
public class Dup{ public class Dup{
… 冲突 … 依赖库之间包含冲突类
(b) 类型 1.2 } }
依赖于 依赖于
依赖库 lib1 依赖库 lib3: v1.0.0
依赖于
模块 mod
依赖于
冲突
依赖树中有冲突版本的同一依赖库
(c) 类型 1.3 依赖库 lib2 依赖库 lib3: v2.0.0
(d) 类型 1.4
import lib2.A mod 直接使用传递依赖 lib2
A classA = new A(); 中的类但未在配置文件中声明
包含代码
lib2.A
依赖于 依赖于 包含类 public class A {
... 依赖库使用但未声明
}
模块 mod 依赖库 lib1:4.0.0 依赖库 lib2
(e) 类型 1.5
依赖于
模块 mod 依赖库 lib1
mod 声明 lib1 为依赖库, 但并未使用其中的类 依赖库声明但未使用
<dependency>
依赖于 相关配置 <groupId>example</groupId>
<artifactId>lib</artifactId>
<version>1.0.0</version>
模块 mod 依赖库 lib </dependency> 未声明依赖范围, 默认为 compile
预期依赖范围: test 实际依赖范围: compile 实际范围与预
(f) 类型 1.6 冲突 期范围冲突
依赖于 依赖于
模块 mod 依赖于 依赖库 lib1 依赖库 lib2:compile
依赖树中依赖库有冲突的依赖范围
冲突
(g) 类型 1.7 依赖库 lib2: test
(h) 类型 1.8
<dependency>
依赖于 Maven 相关配置 <groupId>example</groupId>
<artifactId>lib</artifactId>
<version>1.0.0</version>
模块 mod 依赖库 lib Gradle 相关配置 </dependency>
依赖库在 Maven 和
冲突
同时使用 Maven 和 Gradle Gradle 中版本不一致
implementation “example:lib: 2.0.0”
图 A1 模块粒度依赖异味特征实例
3) 类别 1.3 库版本冲突: 模块的依赖树中存在同一依赖库的不同版本 (7/47). 模块的依赖树中存在组织名和构
件名都相同但版本不同的依赖库. 在图 A1(c) 对应的#SELENIDE-1652 [42] 中, 模块的直接依赖 browsermob-core 和
selenide 都引入了 netty-all 作为传递依赖, 但 netty-all 的版本不同, 最终只有一个版本的 netty-all 被选择, 其余会被
忽略, 最终导致 NoSuchMethodError. 最终开发者移除了直接依赖 browsermob-core 解决了版本冲突.
这类异味在 Maven 和 Gradle 中皆存在, 产生原因主要在于依赖管理工具的版本调停机制. 当依赖树中存在同
一依赖库的不同版本时, 最终只有一个版本的依赖库会被选择. 然而, 在 Maven 和 Gradle 下, 这类异味有不同的表

