本文介绍依赖项版本冲突以及如何对其进行故障排除。
适用于 Java 的 Azure 客户端库依赖于常用的第三方库,例如以下库:
许多 Java 应用程序和框架直接或可传递地使用这些库,这会导致 版本冲突。 依赖项管理器(如 Maven 和 Gradle)解析所有依赖项,以确保类路径中每个依赖项只有一个版本。 但是,不能保证解析的依赖项版本与应用程序中该依赖项的所有使用者兼容。 有关详细信息,请参阅 Maven 文档中的 依赖项机制简介 和 Gradle 文档中的 了解依赖项解析。
直接依赖项的 API 不兼容会导致编译错误。 钻石依赖项 不兼容通常会导致运行时失败,例如 NoClassDefFoundError、 NoSuchMethodError 或其他 LinkageError。 并非所有库都严格遵循 语义版本控制,并且中断性变更有时发生在同一主要版本中。
诊断版本不匹配问题
以下部分介绍了如何诊断版本不匹配问题的方法。
使用 Azure SDK for Java 生成工具
在《Azure SDK 和 Apache Maven 入门》中介绍的 Azure SDK for Java 构建工具有助于识别常见的构建问题。 建议您将此构建工具添加到项目中,并在常规构建过程中通过添加 azure:run
Maven 目标来运行它。 通过适当的配置,可以更主动地识别和解决依赖冲突,从而在运行时之前防止问题的出现。
查看依赖项树
运行 mvn dependency:tree
或 gradle dependencies --scan
显示应用程序的完整依赖项树,版本号为。
mvn dependency:tree -Dverbose
提供更多信息,但可能会误导。 有关详细信息,请参阅 Maven 文档中的 Apache Maven 依赖项树 。 对于怀疑存在版本冲突的每个库,请记下其版本号,并确定哪些组件依赖于它。
开发和生产环境中的依赖项解析可能以不同的方式工作。 Apache Spark、 Apache Flink、 Databricks 和 IDE 插件需要为自定义依赖项配置额外的配置。 他们还可以带上自己的 Azure 客户端库版本或通用组件。 如需了解更多信息,请参阅以下文章:
- 捆绑应用程序的 Apache Spark 依赖项
- Apache Flink 的项目配置
- 适用于 Databricks 的如何在 Databricks 中正确更新 Maven 库
有关此类环境中的冲突解决的详细信息,请参阅本文后续的创建胖 JAR 部分。
配置 Azure Functions
Azure Functions 上的内部依赖项版本(仅运行 Java 8)优先于用户提供的依赖项版本。 这种依赖关系会导致版本冲突,特别是 Jackson、Netty 和 Reactor。
若要解决此问题,请将 FUNCTIONS_WORKER_JAVA_LOAD_APP_LIBS
环境变量 true
设置为或 1
。 请务必将 Azure 函数工具(v2 或 v3)更新到最新版本。
注释
此配置仅适用于运行 Java 8 的 Azure Functions,运行 Java 11 的 Functions 不需要特殊配置。
配置 Apache Spark
适用于 Java 的 Azure SDK 支持多个版本的 Jackson,但有时可能会出现问题,具体取决于生成工具及其依赖项解析顺序。 此问题的一个很好的示例是 Apache Spark,从版本 3.0.0 开始及更高版本,依赖于 Jackson 2.10。 虽然它与适用于 Java 的 Azure SDK 兼容,但开发人员通常会发现通常使用的是更新版本的 Jackson,从而导致不兼容性问题。 要缓解此问题,应固定特定版本的 Jackson(与 Spark 兼容的版本)。 有关详细信息,请参阅本文中的 “支持多个 Jackson 版本 ”部分。
如果使用早期版本的 Spark,或者如果使用的另一个库需要适用于 Java 的 Azure SDK 不支持的更低版本的 Jackson,请继续阅读本文,了解可能的缓解步骤。
检测 Jackson 运行时版本
在 Azure Core 1.21.0 中,我们添加了杰克逊运行时版本的运行时检测和更好的诊断。
如果看到 LinkageError
(或其任何子类)与 Jackson API 相关,请检查运行时版本信息的异常消息。 例如:com.azure.core.implementation.jackson.JacksonVersionMismatchError: com/fasterxml/jackson/databind/cfg/MapperBuilder Package versions: jackson-annotations=2.9.0, jackson-core=2.9.0, jackson-databind=2.9.0, jackson-dataformat-xml=2.9.0, jackson-datatype-jsr310=2.9.0, azure-core=1.19.0-beta.2
从 JacksonVersion
中查找警告和错误日志。 有关详细信息,请参阅 Azure SDK for Java 中的配置日志记录。 例如:[main] ERROR com.azure.core.implementation.jackson.JacksonVersion - Version '2.9.0' of package 'jackson-core' is not supported (too old), please upgrade.
注释
检查所有 Jackson 包是否具有相同的版本。
有关 Azure SDK 使用的包列表和支持的 Jackson 版本,请参阅“ 支持多个 Jackson 版本 ”部分。
缓解版本不匹配问题
以下部分介绍如何缓解版本不匹配问题。
使用 Azure SDK BOM
使用最新的稳定 Azure SDK BOM ,但不在 POM 文件中指定 Azure SDK 和依赖项版本。 如果适用,请使用 Azure Spring Boot BOM。
Azure SDK BOM 中列出的依赖项经过严格测试,以避免依赖项冲突。
避免不必要的依赖项
如果可以,请删除依赖项。 有时,应用程序依赖于提供基本相同功能的多个库。 不必要的依赖项会使应用程序暴露于安全漏洞、版本冲突以及支持和维护成本之中。
更新依赖项版本
如果切换到最新的 Azure SDK BOM 无济于事,请确定导致冲突的库以及使用这些库的组件。 (有关详细信息,请参阅本文前面的 “查看依赖项树 ”部分。尝试更新到较新版本,该版本可防范安全漏洞,并且通常会带来新功能、性能改进和 bug 修复。
避免降级 Azure SDK 版本,因为这样可能会使应用程序容易受到已知漏洞和问题的影响。
分装库
有时,没有任何库可以组合在一起使用,而分装成了最后的选择。
注释
着色具有重大缺点:它增加了类路径上的包大小和类数,使代码导航和调试变得困难,不会重定位 JNI 代码,中断反射,并可能违反代码许可证等。 只有在其他选项用尽之后,才应使用它。
着色使你可以在生成时在 JAR 中包含依赖项,然后重命名包并更新应用程序代码以在着色位置使用代码。 钻石依赖项冲突不再是问题,因为依赖项有两个不同的副本。 可以着色具有冲突的可传递依赖项或直接应用程序依赖项的库,如以下列表所述:
-
可传递依赖项冲突:例如,第三方库
A
需要 Jackson 2.9,Azure SDK 不支持,并且无法更新A
。 创建一个新模块,其中包括A
并会分装(重定位)Jackson 2.9 以及(可选)A
的其他依赖项。 - 应用程序依赖项冲突:应用程序直接使用 Jackson 2.9。 更新代码时,可改为将 Jackson 2.9 分装并重定位到具有已重定位的 Jackson 类的新模块中。
注释
使用重定位的 Jackson 类创建胖 JAR 并不能解决这些示例中的版本冲突,它只会强制使用单个分装版本的 Jackson。
创建胖 JAR
Databricks 或 Apache Spark 等环境具有自定义依赖项管理,并提供像 Jackson 这样的常见库。 为避免与提供的库发生冲突,可能需构建一个包含所有依赖项的胖 JAR。 有关详细信息,请参阅 Apache Maven 着色插件。 在许多情况下,重新定位杰克逊类(com.fasterxml.jackson
)可以缓解此问题。 有时,此类环境也会自带 Azure SDK 版本,因此可能需要重新定位 com.azure
命名空间以解决版本冲突。
了解兼容的依赖项版本
有关 azure-core
的特定依赖项及其版本的信息,请参阅 Maven 中央库中的 azure-core。 下表显示了一些常规注意事项:
依赖 | 支持的版本 |
---|---|
杰克逊 | 2.10.0 和更新的次要版本兼容。 有关详细信息,请参阅 “支持多个 Jackson 版本 ”部分。 |
SLF4J | 1.7.* |
netty-tcnative-boringssl-static(Netty高性能计算库) | 2.0.* |
Netty-Common(网络通用组件) | 4.1.* |
reactor-core | 3.X.* - 主要版本号和次要版本号必须与版本 azure-core 所依赖的版本完全匹配。 有关详细信息,请参阅有关弃用的项目反应器策略。 |
支持多个 Jackson 版本
适用于 Java 的 Azure SDK 支持使用一系列 Jackson 版本。 支持的最低版本是 Jackson 2.10.0。 适用于 Java 的 Azure SDK 客户端库根据运行时检测到的版本调整其配置和杰克逊使用情况。 此调整可实现与较旧版本的 Spring 框架、Apache Spark 和其他常见环境的更大兼容性。 应用程序可以降级 Jackson 版本(到 2.10.0 或更高版本),而不会中断适用于 Java 客户端库的 Azure SDK。
注释
使用旧版本的 Jackson 可能会使应用程序面临已知的漏洞和问题。 有关详细信息,请参阅 Jackson 库的已知漏洞列表。
指定特定版本的 Jackson 时,请确保对 Azure SDK 使用的所有模块执行此操作,如下列表所示:
jackson-annotations
jackson-core
jackson-databind
jackson-dataformat-xml
jackson-datatype-jsr310
从 Jackson 迁移到 azure-json
适用于 Java 的 Azure 客户端库正在迁移到 azure-json,这不依赖于任何第三方组件,并为 JSON 提供共享基元、抽象和帮助程序。
Apache Spark、Apache Flink 和 Databricks 等环境可能带来尚不依赖azure-core
的azure-json
较旧版本。 因此,在此类环境中使用较新版本的 Azure 库时,可能会遇到类似 java.lang.NoClassDefFoundError: com/azure/json/JsonSerializable
错误。 可以通过添加显式依赖项 azure-json
来缓解此错误。
后续步骤
熟悉依赖项版本冲突以及如何对其进行故障排除后,请参阅 适用于 Java 的依赖项管理 ,了解有关防止冲突的最佳方法的信息。