解决依赖关系版本冲突

本文介绍了依赖关系版本冲突以及如何解决。

适用于 Java 的 Azure 客户端库依赖于常用的第三方库,例如以下库:

许多 Java 应用程序和框架直接或间接地使用这些库,这会导致版本冲突。 依赖关系管理器(例如 MavenGradle)解析所有依赖关系,以便类路径上的每个依赖关系只有一个对应版本。 但是,不保证解析的依赖关系版本与应用程序中该依赖关系的所有使用者兼容。 有关详细信息,请参阅 Maven 文档中的依赖关系机制简介以及 Gradle 文档中的了解依赖关系解析

直接依赖关系的 API 不兼容会导致编译错误。 Diamond 依赖关系不兼容通常会导致运行时故障,如 NoClassDefFoundErrorNoSuchMethodError 或其他 LinkageError。 并非所有库都严格遵循语义版本控制,而同一个主版本内有时会发生中断性变更。

诊断版本不匹配问题

以下部分介绍了如何诊断版本不匹配问题的方法。

使用 Azure SDK for Java 生成工具

Azure SDK for Java 生成工具( Azure SDK 和 Apache Maven 入门)有助于识别常见问题。 建议将此生成工具添加到项目,并通过将 Maven 目标添加到常规生成过程来运行它 azure:run 。 使用适当的配置,可以更主动地识别和解决依赖项冲突,然后再在运行时出现问题。

查看依赖关系树

运行 mvn dependency:treegradle dependencies --scan 以显示应用程序的完整依赖关系树,其中包含版本号。 mvn dependency:tree -Dverbose 提供更多信息,但可能会误导。 有关详细信息,请参阅 Maven 文档中的 Apache Maven 依赖项树 。 对于怀疑存在版本冲突的每个库,请记下其版本号,并确定哪些组件依赖于它。

开发和生产环境中的依赖关系解决方案可能会有所不同。 Apache SparkApache FlinkDatabricks 和 IDE 插件需要用于自定义依赖关系的额外配置。 它们还可以提供自己的 Azure 客户端库或常用组件版本。 有关详细信息,请参阅以下文章:

有关此类环境中冲突解决的详细信息,请参阅本文后面的创建 fat JAR 部分。

配置 Azure Functions

Azure Functions(仅运行 Java 8)上的内部依赖关系版本优先于用户提供的版本。 此依赖关系会导致版本冲突,特别是对于 Jackson、Netty 和 Reactor。

若要解决此问题,请将 FUNCTIONS_WORKER_JAVA_LOAD_APP_LIBS 环境变量设置为 true1。 请确保将 Azure Function Tools(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 中,我们添加了运行时检测并且能更好地诊断 Jackson 运行时版本。

如果看到 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 中包含依赖项,然后重命名包并更新应用程序代码以在着色位置使用代码。 由于依赖关系存在两个不同副本,因此 Diamond 依赖关系冲突不再是问题。 你可以为具有冲突的可传递依赖关系或直接应用程序依赖关系的库着色,如以下列表中所述:

  • 可传递依赖项冲突:例如,第三方库 A 需要 Jackson 2.9,Azure SDK 不支持,并且无法更新 A。 创建一个新模块,其中包括 A并为 Jackson 2.9 和(可选)A 的其他依赖关系着色(重新定位)。
  • 应用程序依赖项冲突:应用程序直接使用 Jackson 2.9。 在更新代码时,可以改为将 Jackson 2.9 着色并重新定位到具有重新定位的 Jackson 类的新模块中。

注意

使用重定位的 Jackson 类创建 fat JAR 不能解决此类示例中的版本冲突,只会强制使用单个着色版本的 Jackson。

创建 fat JAR

Databricks 或 Apache Spark 等环境具有自定义依赖关系管理并提供 Jackson 等常用库。 若要避免与提供的库冲突,可能需要构建包含所有依赖关系的 fat JAR。 有关详细信息,请参阅 Apache Maven 着色插件。 在许多情况下,重定位 Jackson 类 (com.fasterxml.jackson) 能缓解此问题。 有时,此类环境还会引入自己的 Azure SDK 版本,因此可能会强制重定位 com.azure 命名空间以解决版本冲突。

了解兼容的依赖关系版本

有关特定依赖项及其版本的信息 azure-core,请参阅 Maven 中央存储库中的 azure 核心 。 下表显示了一些一般注意事项:

依赖项 支持的版本
Jackson 2.10.0 和较新的次要版本是兼容的。 有关详细信息,请参阅“ 支持多个 Jackson 版本 ”部分。
SLF4J 1.7.*
netty-tcnative-boringssl-static 2.0.*
netty-common 4.1.*
reactor-core 3.X.* - 主版本号和次版本号必须与 azure-core 版本所依赖的版本号完全匹配。 有关详细信息,请参阅 Project Reactor 弃用政策

支持多个 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-jsonazure-core较旧版本。 因此,在此类环境中使用较新版本的 Azure 库时,可能会遇到类似 java.lang.NoClassDefFoundError: com/azure/json/JsonSerializable错误。 可以通过添加显式依赖项 azure-json来缓解此错误。

后续步骤

现在你熟悉了依赖关系版本冲突以及如何解决,请参阅 Java 的依赖关系管理以了解防止此类冲突的最佳方法。