Solucionar conflitos de versão de dependência
Este artigo descreve conflitos de versão de dependência e como solucioná-los.
As bibliotecas de cliente do Azure para Java dependem de bibliotecas de terceiros populares, como as seguintes:
Muitas aplicações e frameworks Java usam essas bibliotecas direta ou transitivamente, 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 de dependência Diamond geralmente resulta em falhas de 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.
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.
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. Eles também podem trazer suas próprias versões das bibliotecas do Cliente do Azure ou componentes comuns. Para obter mais informações, consulte os seguintes artigos:
- Agregando as dependências do seu aplicativo para o Apache Spark
- Configuração do projeto para Apache Flink
- Como atualizar corretamente uma biblioteca Maven no Databricks for Databricks
Para obter mais informações sobre a resolução de conflitos nesses ambientes, consulte a seção Criar um JAR gordo mais adiante neste artigo.
Configurar o Azure Functions
A versão de dependência interna no Azure Functions (executando somente Java 8) tem precedência sobre uma versão fornecida pelo usuário. 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.
Nota
Esta configuração aplica-se apenas ao Azure Functions que executa Java 8, as Functions que executam o Java 11 não precisam de configuração especial.
Configurar o 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 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 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 obter possíveis etapas de atenuação.
Detetar a versão do tempo de execução de Jackson
No Azure Core 1.21.0, adicionamos deteção de tempo de execução e melhores diagnósticos da versão de tempo de execução de Jackson.
Se você vir LinkageError
(ou qualquer uma de suas subclasses) relacionada à API Jackson, verifique a mensagem da exceção para obter informações sobre a versão do tempo 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
Procure logs de aviso e erro do JacksonVersion
. Para obter mais informações, consulte Configurar o 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.
Nota
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 Jackson suportadas, consulte a seção Suporte para várias versões do Jackson.
Atenuar problemas de incompatibilidade de versão
As seções a seguir descrevem como mitigar problemas de incompatibilidade de versão.
Usar a lista técnica do SDK do Azure
Use a lista técnica estável mais recente do SDK do Azure e não especifique o SDK do Azure e as versões de dependência em seu arquivo POM. Quando aplicável, use a lista técnica do Azure Spring Boot.
As dependências listadas na lista técnica do SDK do Azure são testadas rigorosamente para evitar conflitos de dependência.
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 a lista técnica mais recente do SDK do Azure não ajudar, identifique as bibliotecas que causam conflitos e os componentes que as utilizam. (Para mais informações, consulte o Exiba uma seção de á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 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 trabalham juntas, e o sombreamento vem como último recurso.
Nota
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ê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 atualizarA
o . Crie um novo módulo, que incluiA
e sombreia (realoca) Jackson 2.9 e, opcionalmente, outras dependências doA
. - Conflito de dependência do aplicativo: seu aplicativo usa Jackson 2.9 diretamente. Enquanto você está trabalhando na atualização do seu código, você pode sombrear e realocar Jackson 2.9 em um novo módulo com classes Jackson realocadas.
Nota
Criar JAR gordo com classes Jackson realocadas não resolve um conflito de versão nesses exemplos - apenas força uma única versão sombreada de 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. Às vezes, esses ambientes também trazem sua própria versão dos SDKs do Azure, então você pode ser obrigado a realocar com.azure
o namespace para contornar conflitos de versão.
Compreender as versões de dependência compatíveis
Para obter informações sobre azure-core
dependências específicas e suas versões, consulte azure-core no Maven Central Repository. A tabela a seguir mostra algumas considerações gerais:
Dependency | Versões suportadas |
---|---|
Sylvio | 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 Reator sobre depreciações. |
Suporte para várias versões 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 o uso do Jackson dependendo da versão detetada no 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.
Nota
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 3rd e oferece primitivos, abstrações e auxiliares compartilhados para JSON.
Ambientes como Apache Spark, Apache Flink e Databricks podem trazer versões mais antigas do que ainda não dependem do azure-core
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 no azure-json
.
Próximos passos
Agora que você está familiarizado com conflitos de versão de dependência e como solucioná-los, consulte Gerenciamento de dependência para Java para obter informações sobre a melhor maneira de evitá-los.