Versión conflictiva de la biblioteca en un proyecto de Java Maven

Al construir un proyecto maven que tiene muchas dependencias, algunas de esas dependencias dependen de la misma biblioteca pero usan una versión diferente que está causando errores al ejecutar una aplicación.

Por ejemplo, si agrego dos dependencias de proyecto diferentes, A y B que dependen del cliente HTTP de apache pero cada una en una versión diferente, una vez que el cargador de clases carga las clases de cliente http de Apache commons, B intentará usarlas desde ya están cargados por el cargador de clases.

Pero el bytecode de B depende de una versión diferente de las clases cargadas, lo que causa problemas múltiples al ejecutar la aplicación. Uno común es la excepción de método no encontrado (ya que la versión de A del cliente http ya no usa un método específico).

¿Cuál es la estrategia general al construir para evitar tales conflictos? ¿Se debe verificar manualmente el árbol de dependencias para descubrir qué bibliotecas comunes colisionan entre sí?

Bienvenido al infierno de la dependencia maven , como se lo conoce con cariño. Este es un problema bastante común a medida que los proyectos crecen y se introducen más dependencias externas.

Además de Apache Commons (mencionado en su pregunta original), los marcos de registro (log4j, slf4j) son otro culpable frecuente.

Estoy de acuerdo con el consejo dado por “matts” sobre cómo resolver conflictos una vez identificados. En términos de capturar estos conflictos de versiones temprano, también puedes utilizar el complemento maven “enforcer”. Consulte la configuración “dependencyConvergence” . También vea esta publicación SO .

Al usar el complemento de enforcer se producirá un error en la comstackción inmediatamente en el conflicto de versiones, lo que le ahorrará las verificaciones manuales. Esta es una estrategia agresiva, pero previene el tipo de problemas de tiempo de ejecución que provocaron su pregunta / publicación. Al igual que cualquier otra cosa, el complemento de enforcer tiene ventajas y desventajas. Empezamos a usarlo el año pasado, pero luego descubrimos que puede ser una bendición y una maldición. Muchas versiones de libs / frameworks son compatibles con versiones anteriores, por lo que depender (ya sea directa o indirectamente) tanto en la versión 1.2.3 como en la 1.2.4 suele ser correcto tanto en tiempo de comstackción como en tiempo de ejecución. Sin embargo, el complemento de enforcer marcará este conflicto y requerirá que declare exactamente qué versión desea. Suponiendo que el número de conflictos de dependencia es pequeño, esto no requiere mucho trabajo. Sin embargo, una vez que introduces un marco grande (por ejemplo, Spring MVC) puede volverse desagradable.

Espero que sea información útil.

Puede usar el objective del tree del complemento de dependencia Maven para visualizar todas las dependencias transitivas en su proyecto y buscar dependencias que digan “omitido para el conflicto”. 1

 mvn dependency:tree -Dverbose mvn dependency:tree -Dverbose | grep 'omitted for conflict' 

Una vez que sepa qué dependencia tiene conflictos de versión, puede usar el parámetro includes para mostrar solo las dependencias que conducen a esa para ver cómo se está extrayendo una dependencia en particular. Por ejemplo, un proyecto en el que A capta las diferentes versiones de C por A y B:

 mvn dependency:tree -Dverbose -Dincludes=project-c [INFO] com.my-company:my-project:jar:1.0-SNAPSHOT [INFO] +- project-a:project-a:jar:0.1:compile [INFO] | \- project-c:project-c:jar:1.0:compile [INFO] \- project-b:project-b:jar:0.2:compile [INFO] \- project-x:project-x:jar:0.1:compile [INFO] \- (project-c:project-c:jar:2.0:compile - omitted for conflict) 

Para resolver realmente el conflicto, en algunos casos es posible encontrar una versión de la dependencia transitiva con la que trabajarán sus dos dependencias principales. Agregue la dependencia transitiva a la sección de gestión de dependencyManagement de su pom e intente cambiar la versión hasta que funcione.

Sin embargo, en otros casos, puede que no sea posible encontrar una versión de la dependencia que funcione para todos. En estos casos, es posible que tenga que retroceder la versión en una de las dependencias principales para hacer que use una versión de la dependencia transitiva que funcione para todos. Por ejemplo, en el ejemplo anterior, A 0.1 usa C 1.0 y B 0.2 usa C 2.0. Supongamos que C 1.0 y 2.0 son completamente incompatibles. Pero tal vez es posible que su proyecto use B 0.1 en su lugar, que depende de C 1.5, que es compatible con C 1.0.

Por supuesto, estas dos estrategias no siempre funcionarán, pero he encontrado el éxito con ellas antes. Otras opciones más drásticas incluyen empaquetar su propia versión de la dependencia que corrige la incompatibilidad o tratar de aislar las dos dependencias en cargadores de clases separados.

Puede usar el complemento maven-enforcer en su pom para forzar versiones específicas de las dependencias transitivas. Esto lo ayudaría a evitar omisiones por la configuración de pom cuando hay conflictos.

Esto es lo que funcionó para mí, y pude cambiar las versiones para que coincidan. Si no puede cambiar las versiones, esto no será muy útil.

Convergencia de Dependencia

  ...   ...  org.apache.maven.plugins maven-enforcer-plugin 1.4   enforce       enforce     ...   ...  

Forzar una versión en la dependencia usando corchetes:

  org.slf4j slf4j-api compile [1.0.0]  

Me gustaría extender las respuestas de Todd y Matts con el hecho de que puedes:

  • mvn dependency:tree -Dverbose -Dincludes=project-c

  • Agregue una etiqueta para todas sus dependencias que tengan una dependencia transitiva de project-c .

  • O, como alternativa, dentro de su proyecto, defina explícitamente project-c como una dependencia para anular los transitivos y evitar conflictos. (Esto aún se mostrará en tu árbol cuando uses `-Dverbose).

Alternativamente, si esos proyectos están bajo su control, simplemente puede actualizar la versión de project-c .

Intereting Posts