Compartir vía


Solución de problemas de conflictos de versiones de dependencias

En este artículo, se describen los conflictos de versiones de dependencias y cómo solucionarlos.

Las bibliotecas cliente de Azure para Java dependen de bibliotecas populares de terceros, como las siguientes:

Muchas aplicaciones y marcos de trabajo de Java usan estas bibliotecas de forma directa o transitiva, lo que conduce a conflictos de versiones. Los administradores de dependencias, como Maven y Gradle, resuelven todas las dependencias para que solo haya una sola versión de cada dependencia en la ruta de acceso de clases. Sin embargo, no se garantiza que la versión de la dependencia resuelta sea compatible con todos los consumidores de esa dependencia en la aplicación. Para más información, consulte Introducción al mecanismo de dependencias en la documentación de Maven y Descripción de la resolución de dependencias en la documentación de Gradle.

La incompatibilidad de la API con las dependencias directas produce errores de compilación. La incompatibilidad de dependencias en rombo suele dar lugar a errores en tiempo de ejecución como NoClassDefFoundError, NoSuchMethodError u otros de tipo LinkageError. No todas las bibliotecas siguen estrictamente el versionamiento semántico y los cambios importantes a veces se producen dentro de la misma versión principal.

Diagnóstico de problemas de error de coincidencia de versiones

En las secciones siguientes se describen métodos sobre cómo diagnosticar problemas de coincidencia de versiones.

Uso de la herramienta de compilación del SDK de Azure para Java

La herramienta de compilación del SDK de Azure para Java, que se introdujo en Introducción al SDK de Azure y Apache Maven, ayuda a identificar problemas comunes. Se recomienda agregar esta herramienta de compilación al proyecto y ejecutarla agregando el azure:run destino de Maven al proceso de compilación normal. Con la configuración adecuada, puede identificar y resolver conflictos de dependencia de forma más proactiva, antes de que se conviertan en problemas en tiempo de ejecución.

Visualización de un árbol de dependencias

Ejecute mvn dependency:tree o gradle dependencies --scan para mostrar el árbol de dependencias completo de la aplicación, con los números de versión. mvn dependency:tree -Dverbose proporciona más información, pero puede ser engañosa. Para más información, consulte Árbol de dependencias de Apache Maven en la documentación de Maven. Para cada biblioteca sospechosa de que tiene un conflicto de versión, anote su número de versión y determine qué componentes dependen de ella.

La resolución de dependencias en entornos de desarrollo y producción puede funcionar de manera diferente. Apache Spark, Apache Flink, Databricks y los complementos del IDE necesitan una configuración adicional para las dependencias personalizadas. También pueden traer sus propias versiones de las bibliotecas cliente de Azure o componentes comunes. Vea los siguientes artículos para más información:

Para más información sobre la resolución de conflictos en estos entornos, consulte la sección Creación de un archivo fat-JAR más adelante en este artículo.

Configuración de Azure Functions

La versión de las dependencias internas en Azure Functions (solo en Java 8) tiene prioridad sobre la proporcionada por el usuario. Esta dependencia provoca conflictos de versiones, especialmente con Jackson, Netty y Reactor.

Para solucionar este problema, establezca la variable de entorno FUNCTIONS_WORKER_JAVA_LOAD_APP_LIBS en true o 1. Asegúrese de actualizar las herramientas de Azure Functions (v2 o v3) a la versión más reciente.

Nota:

Esta configuración se aplica solo a Azure Functions cuando se ejecuta en Java 8, las funciones que se ejecutan en Java 11 no necesitan una configuración especial.

Configuración de Apache Spark

El SDK de Azure para Java admite varias versiones de Jackson, pero a veces pueden surgir problemas en función de las herramientas de compilación y su orden de resolución de dependencias. Un buen ejemplo de este problema es con Apache Spark, versión 3.0.0 y posteriores, que depende de Jackson 2.10. Aunque es compatible con el SDK de Azure para Java, los desarrolladores a menudo descubren que se usa una versión más reciente de Jackson en su lugar, lo que da lugar a incompatibilidades. Para mitigar este problema, debe anclar una versión específica de Jackson (una compatible con Spark). Para obtener más información, consulte la sección Compatibilidad con varias versiones de Jackson de este artículo.

Si usa versiones anteriores de Spark o si otra biblioteca que usa requiere una versión incluso anterior de Jackson que el SDK de Azure para Java no admite, siga leyendo este artículo para conocer los posibles pasos de mitigación.

Detección de la versión del entorno de ejecución de Jackson

En Azure Core 1.21.0, hemos agregado detección del entorno de ejecución y mejores diagnósticos de la versión del entorno de ejecución de Jackson.

Si aparece la excepción LinkageError (o cualquiera de sus subclases) relacionado con la API de Jackson, compruebe el mensaje de la excepción para obtener información de la versión del entorno de ejecución. Por ejemplo: 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

Busque registros de errores y advertencias de JacksonVersion. Para más información, consulte Configuración del registro en Azure SDK para Java. Por ejemplo: [main] ERROR com.azure.core.implementation.jackson.JacksonVersion - Version '2.9.0' of package 'jackson-core' is not supported (too old), please upgrade.

Nota:

Compruebe que todos los paquetes de Jackson tienen la misma versión.

Para obtener la lista de paquetes usados por el SDK de Azure y las versiones compatibles de Jackson, consulte la sección Compatibilidad con varias versiones de Jackson.

Mitigación de problemas de error de coincidencia de versiones

En las secciones siguientes, se describe cómo mitigar problemas de error de coincidencia de versiones.

Uso de BOM de Azure SDK

Use la versión estable más reciente de BOM de Azure SDK y no especifique las versiones de dependencias y Azure SDK en el archivo POM. Si procede, use BOM de Azure Spring Boot.

Las dependencias enumeradas en BOM de Azure SDK se han probado rigurosamente para evitar conflictos de dependencias.

Evitar las dependencias innecesarias

Quite las dependencias, si es posible. A veces, una aplicación tiene dependencias de varias bibliotecas que proporcionan esencialmente la misma funcionalidad. Estas dependencias innecesarias exponen las aplicaciones a vulnerabilidades de seguridad, conflictos de versiones y costos de soporte técnico y mantenimiento.

Actualización de las versiones de las dependencias

Si cambiar a la boM más reciente del SDK de Azure no ayuda, identifique las bibliotecas que causan conflictos y los componentes que los usan. (Para obtener más información, consulte Vea una sección de árbol de dependencias anteriormente en este artículo). Intente actualizar a una versión más reciente, que protege contra vulnerabilidades de seguridad y, a menudo, aporta nuevas características, mejoras de rendimiento y correcciones de errores.

Evite cambiar a una versión anterior de Azure SDK, ya que puede exponer la aplicación a vulnerabilidades y problemas conocidos.

Bibliotecas en la sombra

A veces, no hay ninguna combinación de bibliotecas que funcionen juntas y el uso de bibliotecas en la sombra es el último recurso.

Nota:

El sombreado tiene inconvenientes significativos: aumenta el tamaño del paquete y el número de clases en la ruta de clase, hace que la navegación de código y la depuración sean difíciles, no reubica el código JNI, interrumpe la reflexión y puede infringir licencias de código entre otras cosas. Solo se debe usar después de que se agoten otras opciones.

El sombreado permite incluir dependencias dentro de un archivo JAR en tiempo de compilación y, a continuación, cambiar el nombre de los paquetes y actualizar el código de la aplicación para usar el código en la ubicación sombreada. El conflicto de dependencias en rombo ya no es un problema, porque hay dos copias diferentes de una dependencia. Puede poner en la sombra una biblioteca que tenga una dependencia transitiva en conflicto o una dependencia de aplicación directa, como se describe en la lista siguiente:

  • Conflicto de dependencia transitiva: por ejemplo, la biblioteca A de terceros requiere Jackson 2.9, que los SDK de Azure no admiten y no es posible actualizar A. Cree un nuevo módulo, que incluya A y ponga en la sombra (reubicar) la versión 2.9 de Jackson y, opcionalmente, otras dependencias de A.
  • Conflicto de dependencias de la aplicación: la aplicación usa Jackson 2.9 directamente. Mientras trabaja para actualizar el código, puede sombrear y reubicar Jackson 2.9 en un nuevo módulo con clases de Jackson reubicadas en su lugar.

Nota:

La creación de un archivo fat-JAR con clases de Jackson reubicadas no resuelve el conflicto de versiones de estos ejemplos: solo fuerza una única versión en la sombra de Jackson.

Creación de un archivo fat-JAR

Los entornos como Databricks o Apache Spark disponen de administración de dependencias personalizada y proporcionan bibliotecas comunes como Jackson. Para evitar conflictos con las bibliotecas proporcionadas, puede que desee crear un archivo fat-JAR que contenga todas las dependencias. Para más información, consulte Complemento Shade de Apache Maven. En muchos casos, la reubicación de las clases de Jackson (com.fasterxml.jackson) mitiga el problema. En ocasiones, estos entornos también traen su propia versión de Azure SDK, por lo que podría tener que reubicar el espacio de nombres com.azure para evitar conflictos de versiones.

Información sobre las versiones de dependencias compatibles

Para obtener información sobre azure-corelas dependencias específicas y sus versiones, consulte azure-core en el repositorio central de Maven. En la tabla siguiente, se muestran algunas consideraciones generales:

Dependencia Versiones compatibles
Jackson 2.10.0 y las versiones secundarias más recientes son compatibles. Para obtener más información, consulte la sección Compatibilidad con varias versiones de Jackson.
SLF4J 1.7.*
netty-tcnative-boringssl-static 2.0.*
netty-common 4.1.*
reactor-core 3.X.*: los números de versión principal y secundaria deben coincidir exactamente con aquellos de los que depende su versión de azure-core. Para más información, consulte la directiva sobre entrada en desuso del proyecto Reactor.

Compatibilidad con varias versiones de Jackson

El SDK de Azure para Java admite el trabajo con una variedad de versiones de Jackson. La versión más baja admitida es Jackson 2.10.0. Las bibliotecas cliente del SDK de Azure para Java ajustan su configuración y el uso de Jackson en función de la versión que se detecte en tiempo de ejecución. Este ajuste permite una mayor compatibilidad con versiones anteriores del marco spring, Apache Spark y otros entornos comunes. Las aplicaciones pueden degradar las versiones de Jackson (a la versión 2.10.0 o posterior) sin interrumpir las bibliotecas cliente de Azure SDK para Java.

Nota:

El uso de versiones anteriores de Jackson puede exponer aplicaciones a vulnerabilidades y problemas conocidos. Para obtener más información, consulte la lista de vulnerabilidades conocidas para las bibliotecas de Jackson.

Al anclar una versión específica de Jackson, asegúrese de hacerlo para todos los módulos usados por Azure SDK, que se muestran en la lista siguiente:

  • jackson-annotations
  • jackson-core
  • jackson-databind
  • jackson-dataformat-xml
  • jackson-datatype-jsr310

Migración de Jackson a azure-json

Las bibliotecas cliente de Azure para Java están en proceso de migración a azure-json, que no depende de ningún componente de terceros y ofrece primitivos compartidos, abstracciones y asistentes para JSON.

Los entornos como Apache Spark, Apache Flink y Databricks pueden traer versiones anteriores de azure-core que aún no dependen de azure-json. Como resultado, cuando se usan versiones más recientes de bibliotecas de Azure en estos entornos, es posible que reciba errores similares a java.lang.NoClassDefFoundError: com/azure/json/JsonSerializable. Para mitigar este error, agregue una dependencia explícita en azure-json.

Pasos siguientes

Ahora que está familiarizado con los conflictos de versiones de dependencias y cómo solucionarlos, consulte Administración de dependencias para Java para obtener información sobre la mejor manera de evitarlos.