Compartilhar via


Solucionar conflitos de versão de dependência

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

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

Muitos aplicativos e estruturas Java usam essas bibliotecas direta ou transitivamente, o que leva a conflitos de versão. Os gerenciadores de dependências, como Maven e Gradle, resolvem todas as dependências para que haja apenas uma versão de cada dependência no classpath. No entanto, não há garantia de 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, confira 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 de API de dependências diretas resulta em erros de compilação. A incompatibilidade de dependência de losango geralmente resulta em falhas de runtime, como NoClassDefFoundError, NoSuchMethodError ou outros LinkageError. Nem todas as bibliotecas seguem estritamente o controle de versão semântico, e as alterações significativas às vezes ocorrem na 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.

Usar a ferramenta de compilação do SDK do Azure para Java

A ferramenta de compilação do SDK do Azure para Java, introduzida em Introdução ao SDK do Azure e ao Apache Maven, ajuda a identificar problemas comumente encontrados. 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.

Exibir 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 dá mais informações, mas pode ser enganoso. Para obter mais informações, consulte Árvore de dependência do Apache Maven na documentação do Maven. Para cada biblioteca suspeita 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 maneira diferente. Apache Spark, Apache Flink, Databricks e plug-ins IDE precisam de configuração extra para dependências personalizadas. Elas também podem trazer suas próprias versões de bibliotecas de cliente do Azure 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, consulte a seção Criar um JAR do fat mais adiante neste artigo.

Configurar o Azure Functions

A versão de dependência interna no Azure Functions (executando somente o Java 8) tem precedência sobre uma fornecida pelo usuário. Essa dependência causa conflitos de versão, especialmente com Jackson, Netty e Reactor.

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

Observação

Essa configuração se aplica apenas a Azure Functions executando o Java 8; Functions executando o Java 11 não precisam de configuração especial.

Configurar Apache Spark

O SDK do Azure para Java dá suporte a várias versões do Jackson, mas às vezes podem surgir problemas dependendo de suas ferramentas de compilação e sua ordem de resolução de dependência. Um bom exemplo desse problema é com o Apache Spark, versão 3.0.0 e posterior, que depende do Jackson 2.10. Embora seja compatível com o SDK do Azure para Java, os desenvolvedores geralmente descobrem que uma versão mais recente do Jackson é usada, 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 você usar versões anteriores do Spark ou se outra biblioteca usada exigir uma versão ainda anterior do Jackson que o SDK do Azure para Java não suporta, continue lendo este artigo para possíveis etapas de mitigação.

Detectar versão do runtime do Jackson

No Azure Core 1.21.0, adicionamos detecção de runtime e melhor diagnóstico da versão de runtime do Jackson.

Se você vir LinkageError (ou qualquer uma das respectivas subclasses) relacionada à API do Jackson, verifique a mensagem da exceção para obter informações de versão de runtime. 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

Procure logs de aviso e erros do JacksonVersion. Para obter mais informações, confira Configurar o registro em log no SDK do Azure 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 obter a lista de pacotes usados pelo SDK do Azure e as versões do Jackson com suporte, consulte a seção Suporte para várias versões do Jackson.

Mitigar problemas de incompatibilidade de versão

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

Usar a BOM do SDK do Azure

Use a BOM do SDK do Azure estável mais recente e não especifique o SDK do Azure e as versões de dependência em seu arquivo POM. Quando aplicável, use a BOM do Azure Spring Boot.

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

Remover as dependências desnecessárias

Se possível, remova as dependências. Às vezes, um aplicativo tem dependências em várias bibliotecas que fornecem essencialmente a mesma funcionalidade. Essas dependências desnecessárias expõem aplicativos a vulnerabilidades de segurança, conflitos de versão e custos de suporte e manutenção.

Atualizar versões de dependência

Se alternar para a BOM mais recente do SDK do Azure não ajudar, identifique as bibliotecas que causam conflitos e os componentes que as usam. (Para obter mais informações, consulte o Exiba uma seção de árvore de dependências 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 SDK do Azure, porque isso pode expor seu aplicativo a vulnerabilidades e problemas conhecidos.

Bibliotecas de sombreamento

Às vezes, não há nenhuma combinação de bibliotecas que funcionem juntas, e o sombreamento é oferecido como o ú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. Ele deve ser usado somente depois que outras opções forem esgotadas.

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

  • Conflito de dependência transitiva: por exemplo, a biblioteca A de terceiros requer o Jackson 2.9, que os SDKs do Azure não suportam, e não é possível atualizar Ao . Crie um módulo, que inclui A e sombreia (realoca) o Jackson 2.9 e, opcionalmente, outras dependências de A.
  • Conflito de dependência do aplicativo: Seu aplicativo usa o Jackson 2.9 diretamente. Enquanto você está trabalhando na atualização de seu código, você pode sombrear e realocar o Jackson 2.9 em um novo módulo com classes Jackson realocadas.

Observação

A criação de um JAR fat com classes do Jackson realocadas não resolve um conflito de versão nesses exemplos; ela apenas impõe que exista apenas uma versão sombreada do Jackson.

Criar um JAR do fat

Ambientes como Databricks ou Apache Spark têm gerenciamento de dependências personalizado e fornecem bibliotecas comuns, como o Jackson. Para evitar conflitos com as bibliotecas fornecidas, talvez você queira criar um JAR do fat que contenha todas as dependências. Para obter mais informações, confira Plug-in de Sombreamento do Apache Maven. Em muitos casos, a realocação de classes Jackson (com.fasterxml.jackson) atenua o problema. Às vezes, esses ambientes também trazem sua própria versão dos SDKs do Azure, portanto, você pode ser obrigado a realocar o namespace com.azure para solucionar conflitos de versão.

Entender as versões de dependências compatíveis

Para obter informações sobre azure-coredependências específicas e suas versões, consulte azure-core no Repositório Central do Maven. A seguinte tabela 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-common 4.1.*
reactor-core 3.X.* – Os números de versão principal e secundária precisam corresponder exatamente àqueles dos quais sua versão azure-core depende. Para obter mais informações, confira a política para preterimentos do Projeto Reactor.

Suporte para várias versões do Jackson

O SDK do Azure para Java dá suporte ao trabalho com uma variedade de versões do Jackson. A versão mais baixa suportada é Jackson 2.10.0. O SDK do Azure para bibliotecas de cliente Java ajusta sua configuração e uso de Jackson dependendo da versão detectada em tempo de execução. Esse ajuste permite maior compatibilidade com versões mais antigas da estrutura Spring, Apache Spark e outros ambientes comuns. Os aplicativos podem fazer downgrade de versões do Jackson (para 2.10.0 ou superior) sem interromper o SDK do Azure para bibliotecas de cliente 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 fazê-lo para todos os módulos usados pelo SDK do Azure, que são mostrados na lista a seguir:

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

Migração de Jackson para azure-json

As bibliotecas de cliente do Azure para Java estão em processo de migração para azure-json, que não depende de nenhum componente de terceiros 3 e oferece primitivas, abstrações e auxiliares compartilhados para JSON.

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

Próximas etapas

Agora que você está familiarizado com conflitos de versão de dependência e como solucioná-los, confira Gerenciamento de dependências para Java a fim de obter informações sobre a melhor maneira de evitá-los.