Partilhar via


Solucionar conflitos de versão de dependência

Este artigo descreve conflitos de versão de dependência e como solucioná-los.

As bibliotecas clientes do Azure para Java dependem de bibliotecas de terceiros populares, como as seguintes:

Muitas aplicações e frameworks Java utilizam estas bibliotecas de forma direta ou transitiva, o que leva a conflitos de versão. Os gerentes de dependência, como Maven e Gradle , resolvem todas as dependências para que haja apenas uma única versão de cada dependência no classpath. No entanto, não é garantido que a versão de dependência resolvida seja compatível com todos os consumidores dessa dependência em seu aplicativo. Para obter mais informações, consulte Introdução ao mecanismo de dependência na documentação do Maven e Noções básicas sobre resolução de dependência na documentação do Gradle.

A incompatibilidade da API de dependências diretas resulta em erros de compilação. A incompatibilidade das dependências Diamond geralmente resulta em falhas em tempo de execução, como NoClassDefFoundError, NoSuchMethodError ou outro LinkageError. Nem todas as bibliotecas seguem estritamente o controle de versão semântico, e as alterações de quebra às vezes acontecem dentro da mesma versão principal.

Diagnosticar problemas de incompatibilidade de versão

As seções a seguir descrevem métodos sobre como diagnosticar problemas de incompatibilidade de versão.

Use a ferramenta de compilação Azure SDK para Java

A ferramenta Azure SDK for Java build, introduzida em Comece com Azure SDK e Apache Maven, ajuda a identificar problemas comuns. Recomendamos que você adicione essa ferramenta de compilação ao seu projeto e execute-a adicionando o azure:run destino Maven ao seu processo de compilação regular. Com a configuração apropriada, você pode identificar e resolver conflitos de dependência de forma mais proativa, antes que eles se tornem problemas em tempo de execução.

Ver uma árvore de dependência

Execute mvn dependency:tree ou gradle dependencies --scan para mostrar a árvore de dependência completa para seu aplicativo, com números de versão. mvn dependency:tree -Dverbose fornece mais informações, mas pode induzir em erro. Para obter mais informações, consulte Apache Maven Dependency Tree na documentação do Maven. Para cada biblioteca suspeita de ter um conflito de versão, anote seu número de versão e determine quais componentes dependem dela.

A resolução de dependência em ambientes de desenvolvimento e produção pode funcionar de forma diferente. Os plugins Apache Spark, Apache Flink, Databricks e IDE precisam de configuração extra para dependências personalizadas. Também podem trazer as suas próprias versões das bibliotecas do Azure Client ou componentes comuns. Para obter mais informações, consulte os seguintes artigos:

Para obter mais informações sobre a resolução de conflitos nesses ambientes, veja a seção Criar um JAR completo mais adiante neste artigo.

Configurar Azure Functions

A versão interna de dependência do Azure Functions (apenas a correr Java 8) tem precedência sobre uma versão fornecida pelo utilizador. Essa dependência causa conflitos de versão, especialmente com Jackson, Netty e Reator.

Para resolver esse problema, defina a FUNCTIONS_WORKER_JAVA_LOAD_APP_LIBS variável de ambiente como true ou 1. Certifique-se de atualizar as Ferramentas de Função do Azure (v2 ou v3) para a versão mais recente.

Observação

Esta configuração aplica-se apenas ao Azure Functions que executa Java 8; as funções a correr Java 11 não precisam de configuração especial.

Configurar Apache Spark

O Azure SDK para Java suporta múltiplas versões do Jackson, mas por vezes podem surgir problemas dependendo das ferramentas de compilação e da sua ordem de resolução de dependências. Um bom exemplo deste problema é com o Apache Spark, versão 3.0.0 e posterior, que depende do Jackson 2.10. Embora seja compatível com o Azure SDK para Java, os programadores frequentemente descobrem que é usada uma versão mais recente do Jackson, o que resulta em incompatibilidades. Para atenuar esse problema, você deve fixar uma versão específica do Jackson (uma que seja compatível com o Spark). Para obter mais informações, consulte a seção Suporte para várias versões do Jackson neste artigo.

Se usa versões anteriores do Spark, ou se outra biblioteca que utiliza requer uma versão ainda mais antiga do Jackson que o Azure SDK para Java não suporta, continue a ler este artigo para possíveis passos de mitigação.

Detetar a versão do tempo de execução de Jackson

No Azure Core 1.21.0, adicionámos deteção de runtime e melhorias nos diagnósticos da versão do runtime Jackson.

Se vires LinkageError (ou qualquer uma das suas subclasses) relacionada à API Jackson, verifica a mensagem da exceção para obter informações sobre a versão de execução. Por exemplo: 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

Verifique logs de aviso e erro do JacksonVersion. Para mais informações, consulte Configure o registo no Azure SDK para Java. Por exemplo: [main] ERROR com.azure.core.implementation.jackson.JacksonVersion - Version '2.9.0' of package 'jackson-core' is not supported (too old), please upgrade.

Observação

Verifique se todos os pacotes Jackson têm a mesma versão.

Para a lista de pacotes usados pela Azure SDK e as versões Jackson suportadas, veja a secção Suporte para múltiplas versões Jackson.

Atenuar problemas de incompatibilidade de versão

As seções a seguir descrevem como mitigar problemas de incompatibilidade de versão.

Utilizar Azure SDK BOM

Usa a versão estável mais recente Azure SDK BOM e não especifiques versões de Azure SDK e dependências no teu ficheiro POM. Quando aplicável, use o Azure Spring Boot BOM.

As dependências listadas no Azure SDK BOM são rigorosamente testadas para evitar conflitos de dependências.

Evite dependências desnecessárias

Remova dependências, se puder. Às vezes, um aplicativo tem dependências em várias bibliotecas que fornecem essencialmente a mesma funcionalidade. Essas dependências desnecessárias expõem os aplicativos a vulnerabilidades de segurança, conflitos de versão e custos de suporte e manutenção.

Atualizar versões de dependência

Se mudar para o BOM mais recente do Azure SDK não ajudar, identifique as bibliotecas que causam conflitos e os componentes que utilizam essas bibliotecas. (Para obter mais informações, consulte a seção Exibir uma árvore de dependência anteriormente neste artigo.) Tente atualizar para uma versão mais recente, que protege contra vulnerabilidades de segurança e geralmente traz novos recursos, melhorias de desempenho e correções de bugs.

Evite fazer downgrade da versão do Azure SDK porque pode expor a sua aplicação a vulnerabilidades e problemas conhecidos.

Bibliotecas de sombreamento

Às vezes, não há nenhuma combinação de bibliotecas que trabalham juntas, e o sombreamento vem como último recurso.

Observação

O sombreamento tem desvantagens significativas: aumenta o tamanho do pacote e o número de classes no classpath, dificulta a navegação e a depuração de código, não realoca o código JNI, quebra a reflexão e pode violar licenças de código, entre outras coisas. Deve ser utilizado apenas depois de esgotadas as outras opções.

O sombreamento permite incluir dependências em um JAR no momento da compilação, renomear pacotes e atualizar o código do aplicativo para usar o código no local sombreado. O conflito de dependência de diamantes não é mais um problema porque há duas cópias diferentes de uma dependência. Você pode sombrear uma biblioteca que tenha uma dependência transitiva conflitante ou uma dependência direta do aplicativo, conforme descrito na lista a seguir:

  • Conflito de dependências transitivas: Por exemplo, a biblioteca de terceiros A requer Jackson 2.9, que Azure SDKs não suporta, e não é possível atualizar A. Crie um novo módulo, que inclui A e sombreia (realoca) Jackson 2.9 e, opcionalmente, outras dependências do A.
  • Conflito de dependência do aplicativo: seu aplicativo usa Jackson 2.9 diretamente. Enquanto está a trabalhar na atualização do código, pode ocultar e realocar o Jackson 2.9 num novo módulo com classes Jackson realocadas.

Observação

A criação de um JAR gordo com classes Jackson realocadas não resolve um conflito de versão nos exemplos - apenas impõe uma única versão sombreada do Jackson.

Crie um JAR gordo

Ambientes como Databricks ou Apache Spark têm gerenciamento de dependência personalizado e fornecem bibliotecas comuns como Jackson. Para evitar conflitos com bibliotecas fornecidas, convém criar um JAR gordo que contenha todas as dependências. Para obter mais informações, consulte Apache Maven Shade Plugin. Em muitos casos, a realocação de classes Jackson (com.fasterxml.jackson) atenua o problema. Por vezes, esses ambientes também trazem a sua própria versão de Azure SDKs, por isso podes ser obrigado a relocalizar com.azure namespace para contornar conflitos de versões.

Compreender as versões de dependência compatíveis

Para obter informações sobre azure-coredependências específicas e suas versões, consulte azure-core no Maven Central Repository. A tabela a seguir mostra algumas considerações gerais:

Dependência Versões suportadas
Jackson 2.10.0 e versões secundárias mais recentes são compatíveis. Para obter mais informações, consulte a seção Suporte para várias versões do Jackson .
SLF4J 1.7.*
netty-tcnative-boringssl-static 2.0.*
Netty Comum 4.1.*
núcleo do reator 3.X.* - Os números das versões principais e secundárias devem corresponder exatamente aos números da sua azure-core versão depende. Para obter mais informações, consulte a política do Project Reactor sobre desativações.

Suporte para as várias versões de Jackson

O Azure SDK para Java suporta trabalhar com várias versões do Jackson. A versão mais baixa suportada é Jackson 2.10.0. O Azure SDK para bibliotecas cliente Java ajusta a sua configuração e o uso do Jackson consoante a versão detetada em tempo de execução. Esse ajuste permite maior compatibilidade com versões mais antigas da estrutura Spring, Apache Spark e outros ambientes comuns. As aplicações podem fazer downgrade das versões Jackson (para 2.10.0 ou superior) sem quebrar o Azure SDK para bibliotecas clientes Java.

Observação

O uso de versões antigas do Jackson pode expor os aplicativos a vulnerabilidades e problemas conhecidos. Para obter mais informações, consulte a lista de vulnerabilidades conhecidas para bibliotecas Jackson.

Ao fixar uma versão específica do Jackson, certifique-se de o fazer para todos os módulos usados pelo Azure SDK, que são mostrados na lista seguinte:

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

Migração de Jackson para azure-json

Azure bibliotecas cliente para Java estão em processo de migração para azure-json, que não depende de componentes terceiros e oferece primitivas partilhadas, abstrações e auxiliares para JSON.

Ambientes como Apache Spark, Apache Flink e Databricks podem trazer versões mais antigas do que ainda não dependem do azure-coreazure-json. Como resultado, ao usar versões mais recentes de bibliotecas de Azure nestes ambientes, pode aparecer erros semelhantes ao java.lang.NoClassDefFoundError: com/azure/json/JsonSerializable. Você pode atenuar esse erro adicionando uma dependência explícita no azure-json.

Próximos passos

Agora que já está familiarizado com conflitos de versões de dependências e como os diagnosticar, consulte Dependency Management para Java para informações sobre a melhor forma de os evitar.