Page 149 - 《软件学报》2025年第7期
P. 149
3070 软件学报 2025 年第 36 卷第 7 期
这类异味在 Maven 和 Gradle 中皆存在, 其产生原因是主要是项目开发者未对项目构建工具启动器进行配置.
然而, 相较于 Gradle, 在 Maven 中这类问题更容易出现. 这主要是因为 Gradle 的启动器概念早在其诞生之初就已
经存在, 而且官方文档和教程一直都在强调使用启动器的重要性. 此外, 在创建新项目时, Gradle 会自动添加启动
器配置, 进一步推广了其使用. 相反, Maven 是在借鉴了 Gradle 后才添加了启动器功能, 这导致了相对于 Gradle 项
目, Maven 项目中配置缺失的情况更为普遍.
2) 类别 2.4 模块间库重复: 项目多个模块中包含未进行统一管理的同一依赖库 (2/47). 项目多个模块中使用同
一依赖库, 但并未使用构建工具提供的版本管理机制对其版本进行统一管理. 在图 A2(d) 对应的#STARGATE-
235 [93] 中, stargate 在不同的模块中有一系列相同的依赖库, 如 jetty-servlet 等, 但他们的版本声明都分散在各自模
块的配置文件中, 而没有利用 Maven 提供的 dependencyManagement 功能在项目中进行统一管理. 最终开发者取
消了各模块中分散的版本声明, 而是统一进行管理.
这类异味在 Maven 和 Gradle 中皆存在, 其产生原因主要是开发者对 Maven 和 Gradle 提供统一依赖管理机制
的忽略, 它们都提供了相关机制来集中管理项目中的依赖版本, 以确保在不同模块中使用相同的库版本.
3) 类别 2.5 模块间库冲突: 项目多个模块中使用了不同版本的同一依赖库 (1/47). 项目的多个模块中使用了组
织名和构件名都相同但版本不同的依赖库. 在图 A2(e) 对应的#PULSAR-2 642 [94] 中, 项目 pulsar 的模块 pulsar-
client 和模块 pulsar-client-schema 中分别使用了 protobuf 的 2.0 和 3.0 版本. 由于 protobuf:3.0 相较于 2.0 版本能力
更强, pular 计划最终将所有模块中 protobuf 版本升级为 3.0, 但目前尚未完成这一过程, 最终导致模块之间出现依
赖库的版本冲突.
这类异味在 Maven 和 Gradle 中皆存在, 其主要有两种产生原因. 首先, 项目可能考虑到旧版本依赖库兼容性
的考虑或者正处于依赖库的升级过程中, 从而被迫在不同模块中使用不同版本的依赖库, 上例即对应这类产生原
因. 其次, 另一种产生原因是项目中出现了依赖库分散管理的情况 (即异味 2.4), 从而对单一模块依赖库版本的修
改未同步到其他模块, 进而引发模块间的依赖库版本冲突问题.
A2 依赖异味危害与触发场景
在正文中, 我们已经介绍了异味 1.4、1.6 和 1.7 的部分危害与对应触发场景. 下文将继续介绍剩余异味的可
能危害以及异味 1.4、1.6 和 1.7 未在正文中被介绍的危害, 并介绍这些危害对应的触发场景.
1) 类别 1.1 内外类冲突
内外类冲突可能引发一系列严重的后果, 包括但不限于模块构建错误、运行时错误以及运行时语义冲突. 以
下将详细阐述这些危害的具体触发场景.
(1) 构建错误: 其触发场景为模块源码中调用了依赖库中同名冲突类独有的方法. 以图 A3(a) 为例, 模块 mod
和依赖库 lib 中均存在名为 Dup 的冲突类, 但方法 methodLib 仅在 lib 中的 Dup 类中定义. 若模块 mod 试图调用
该方法时, 由于该方法在模块自身的 Dup 类中不存在, 会导致构建错误.
(2) 运行时错误: 其触发场景为模块运行时调用了依赖库中同名冲突类独有的方法. 以图 A3(b) 为例, 与上例
类似, 模块 mod 和依赖库 lib 中同样存在冲突类 Dup, 并且方法 methodLib 同样仅在 lib 中的 Dup 中存在. 但此时
mod 并未直接调用 methodLib, 而是调用了 RunDup 中的 runMethodLib 方法, 间接调用了 methodLib. 此时虽然模
块能正常构建, 但在运行时则无法找到对应方法, 进而造成运行时错误.
(3) 运行时语义冲突: 其触发场景为模块运行时调用了冲突类中签名相同但实现不同的方法. 以图 A3(c) 为例,
模块 mod 和依赖库 lib 中存在冲突类 Dup 和同签名方法 methodLib, 但其具体实现不同. 当 mod 调用 methodLib
时就会出现语义冲突, 预期调用的是 mod 中的 Dup 类, 对应的 methodDup 返回“Module”, 而实际调用的是 Lib 中
的 Dup 类, 对应的 methodDup 返回“Lib”.
2) 类别 1.2 外部类冲突
外部类冲突可能引发一系列严重的后果, 包括但不限于模块构建错误、运行时错误以及运行时语义冲突. 以
下将详细阐述这些危害的具体触发场景.

