Risolvere i conflitti di versione delle dipendenze
Questo articolo descrive i conflitti di versione delle dipendenze e come risolverli.
Le librerie client di Azure per Java dipendono dalle librerie di terze parti più diffuse, ad esempio quelle seguenti:
Molte applicazioni e framework Java usano queste librerie direttamente o transitivamente, che causano conflitti di versione. Le gestioni dipendenze, ad esempio Maven e Gradle , risolvono tutte le dipendenze in modo che sia presente una sola versione di ogni dipendenza dal classpath. Tuttavia, non è garantito che la versione della dipendenza risolta sia compatibile con tutti i consumer di tale dipendenza nell'applicazione. Per altre informazioni, vedere Introduzione al meccanismo di dipendenza nella documentazione di Maven e Informazioni sulla risoluzione delle dipendenze nella documentazione di Gradle.
L'incompatibilità API delle dipendenze dirette genera errori di compilazione. L'incompatibilità delle dipendenze diamond genera in genere errori di runtime, ad esempio NoClassDefFoundError, NoSuchMethodError o un altro LinkageError. Non tutte le librerie seguono rigorosamente il controllo delle versioni semantiche e talvolta le modifiche di rilievo si verificano nella stessa versione principale.
Diagnosticare i problemi di mancata corrispondenza della versione
Le sezioni seguenti descrivono i metodi su come diagnosticare i problemi di mancata corrispondenza della versione.
Usare lo strumento di compilazione azure SDK per Java
Lo strumento di compilazione azure SDK per Java, introdotto in Introduzione ad Azure SDK e Apache Maven, consente di identificare i problemi comunemente riscontrati. È consigliabile aggiungere questo strumento di compilazione al progetto ed eseguirlo aggiungendo la azure:run
destinazione Maven al processo di compilazione regolare. Con la configurazione appropriata, è possibile identificare e risolvere i conflitti di dipendenza in modo più proattivo, prima che diventino problemi in fase di esecuzione.
Visualizzare un albero delle dipendenze
Eseguire mvn dependency:tree
o gradle dependencies --scan
per visualizzare l'albero delle dipendenze completo per l'applicazione, con numeri di versione. mvn dependency:tree -Dverbose
fornisce altre informazioni, ma può essere fuorviante. Per altre informazioni, vedere Apache Maven Dependency Tree nella documentazione di Maven. Per ogni libreria sospetta che abbia un conflitto di versione, annotare il numero di versione e determinare quali componenti dipendono da esso.
La risoluzione delle dipendenze negli ambienti di sviluppo e produzione può funzionare in modo diverso. I plug-in Apache Spark, Apache Flink, Databricks e IDE richiedono una configurazione aggiuntiva per le dipendenze personalizzate. Possono anche usare versioni personalizzate di librerie client di Azure o componenti comuni. Per altre informazioni, vedere gli articoli seguenti:
- Creazione di bundle delle dipendenze dell'applicazione per Apache Spark
- Configurazione del progetto per Apache Flink
- Come aggiornare correttamente una libreria Maven in Databricks per Databricks
Per altre informazioni sulla risoluzione dei conflitti in tali ambienti, vedere la sezione Creare un file JAR fat più avanti in questo articolo.
Configurare Funzioni di Azure
La versione della dipendenza interna in Funzioni di Azure (che esegue solo Java 8) ha la precedenza su una versione fornita dall'utente. Questa dipendenza causa conflitti di versione, in particolare con Jackson, Netty e Reactor.
Per risolvere questo problema, impostare la FUNCTIONS_WORKER_JAVA_LOAD_APP_LIBS
variabile di ambiente su true
o 1
. Assicurarsi di aggiornare Gli strumenti per le funzioni di Azure (v2 o v3) alla versione più recente.
Nota
Questa configurazione si applica solo a Funzioni di Azure che esegue Java 8, Funzioni che eseguono Java 11 non richiedono una configurazione speciale.
Configurare Apache Spark
Azure SDK per Java supporta più versioni di Jackson, ma a volte possono verificarsi problemi a seconda degli strumenti di compilazione e dell'ordinamento della risoluzione delle dipendenze. Un buon esempio di questo problema riguarda Apache Spark, versione 3.0.0 e successive, che dipende da Jackson 2.10. Anche se è compatibile con Azure SDK per Java, gli sviluppatori spesso rilevano che viene usata una versione più recente di Jackson, che comporta incompatibilità. Per attenuare questo problema, è necessario aggiungere una versione specifica di Jackson (una compatibile con Spark). Per altre informazioni, vedere la sezione Supporto per più versioni di Jackson in questo articolo.
Se si usano versioni precedenti di Spark o se un'altra libreria usata richiede una versione ancora precedente di Jackson che Azure SDK per Java non supporta, continuare a leggere questo articolo per i possibili passaggi di mitigazione.
Rilevare la versione del runtime jackson
In Azure Core 1.21.0 è stato aggiunto il rilevamento di runtime e la diagnostica migliore della versione del runtime Jackson.
Se viene visualizzato LinkageError
(o una delle relative sottoclassi) correlate all'API Jackson, controllare il messaggio dell'eccezione per le informazioni sulla versione di runtime. Ad esempio: 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
Cercare i log degli avvisi e degli errori da JacksonVersion
. Per altre informazioni, vedere Configurare la registrazione in Azure SDK per Java. Ad esempio: [main] ERROR com.azure.core.implementation.jackson.JacksonVersion - Version '2.9.0' of package 'jackson-core' is not supported (too old), please upgrade.
Nota
Verificare che tutti i pacchetti Jackson abbiano la stessa versione.
Per l'elenco dei pacchetti usati da Azure SDK e le versioni di Jackson supportate, vedere la sezione Supporto per più versioni di Jackson.
Attenuare i problemi di mancata corrispondenza della versione
Le sezioni seguenti descrivono come attenuare i problemi di mancata corrispondenza della versione.
Usare la distinta base di Azure SDK
Usare la distinta base stabile più recente di Azure SDK e non specificare le versioni di Azure SDK e delle dipendenze nel file POM. Se applicabile, usare la distinta base di Azure Spring Boot.
Le dipendenze elencate nella distinta base di Azure SDK vengono testate rigorosamente per evitare conflitti di dipendenza.
Evitare dipendenze non necessarie
Rimuovere le dipendenze, se possibile. In alcuni casi, un'applicazione ha dipendenze da più librerie che forniscono essenzialmente le stesse funzionalità. Tali dipendenze non necessarie espongono le applicazioni a vulnerabilità di sicurezza, conflitti di versione e costi di supporto e manutenzione.
Aggiornare le versioni delle dipendenze
Se si passa alla versione più recente di Azure SDK BOM non è utile, identificare le librerie che causano conflitti e i componenti che li usano. Per altre informazioni, vedere Visualizzare una sezione relativa all'albero delle dipendenze in precedenza in questo articolo. Provare ad eseguire l'aggiornamento a una versione più recente, che protegge dalle vulnerabilità di sicurezza e spesso offre nuove funzionalità, miglioramenti delle prestazioni e correzioni di bug.
Evitare il downgrade della versione di Azure SDK perché potrebbe esporre l'applicazione a vulnerabilità e problemi noti.
Librerie shade
A volte non esiste alcuna combinazione di librerie che interagiscono e l'ombreggiatura è l'ultima risorsa.
Nota
L'ombreggiatura presenta svantaggi significativi: aumenta le dimensioni del pacchetto e il numero di classi nel classpath, rende difficile lo spostamento del codice e il debug, non riloca il codice JNI, interrompe la reflection e potrebbe violare le licenze di codice tra le altre cose. Deve essere utilizzato solo dopo l'esaurimento di altre opzioni.
L'ombreggiatura consente di includere le dipendenze all'interno di un file JAR in fase di compilazione, quindi rinominare i pacchetti e aggiornare il codice dell'applicazione per usare il codice nel percorso ombreggiato. Il conflitto di dipendenze rombo non è più un problema perché sono presenti due copie diverse di una dipendenza. È possibile ombreggiatura di una libreria con una dipendenza transitiva in conflitto o una dipendenza diretta dell'applicazione, come descritto nell'elenco seguente:
- Conflitto di dipendenze transitive: ad esempio, la libreria
A
di terze parti richiede Jackson 2.9, che gli SDK di Azure non supportano e non è possibile aggiornareA
. Creare un nuovo modulo, che includeA
e shades (riloca) Jackson 2.9 e, facoltativamente, altre dipendenze diA
. - Conflitto di dipendenze dell'applicazione: l'applicazione usa direttamente Jackson 2.9. Mentre si sta lavorando per aggiornare il codice, è possibile ombreggiatura e rilocare Jackson 2.9 in un nuovo modulo con classi Jackson rilocate.
Nota
La creazione di jar fat con classi Jackson rilocate non risolve un conflitto di versione in questi esempi, ma forza solo una singola versione ombreggiata di Jackson.
Creare un file JAR grasso
Gli ambienti come Databricks o Apache Spark hanno una gestione delle dipendenze personalizzata e forniscono librerie comuni come Jackson. Per evitare conflitti con le librerie fornite, è possibile creare un file JAR fat contenente tutte le dipendenze. Per altre informazioni, vedere Plug-in Apache Maven Shade. In molti casi, la rilocazione delle classi Jackson (com.fasterxml.jackson
) riduce il problema. In alcuni casi tali ambienti comportano anche la propria versione degli SDK di Azure, quindi potrebbe essere necessario spostare com.azure
lo spazio dei nomi per risolvere i conflitti di versione.
Informazioni sulle versioni delle dipendenze compatibili
Per informazioni sulle azure-core
dipendenze specifiche e sulle relative versioni, vedere azure-core nel repository centrale Maven. La tabella seguente illustra alcune considerazioni generali:
Dipendenza | Versioni supportate |
---|---|
Jackson | 2.10.0 e versioni secondarie più recenti sono compatibili. Per altre informazioni, vedere la sezione Supporto per più versioni di Jackson. |
SLF4J | 1.7.* |
netty-tcnative-boringssl-static | 2.0.* |
netty-common | 4.1.* |
reattore-core | 3.X.* - I numeri di versione principale e secondaria devono corrispondere esattamente a quelli da cui dipende la versione azure-core . Per altre informazioni, vedere i criteri di Project Reactor sulle deprecate. |
Supporto per più versioni di Jackson
Azure SDK per Java supporta l'uso di una gamma di versioni jackson. La versione più bassa supportata è Jackson 2.10.0. Le librerie client di Azure SDK per Java regolano la configurazione e l'utilizzo di Jackson a seconda della versione rilevata in fase di esecuzione. Questa regolazione consente una maggiore compatibilità con le versioni precedenti di Spring Framework, Apache Spark e altri ambienti comuni. Le applicazioni possono effettuare il downgrade delle versioni jackson (alla versione 2.10.0 o successiva) senza interrompere le librerie client di Azure SDK per Java.
Nota
L'uso di versioni precedenti di Jackson può esporre applicazioni a vulnerabilità e problemi noti. Per altre informazioni, vedere l'elenco delle vulnerabilità note per le librerie Jackson.
Quando si aggiunge una versione specifica di Jackson, assicurarsi di eseguire questa operazione per tutti i moduli usati da Azure SDK, visualizzati nell'elenco seguente:
jackson-annotations
jackson-core
jackson-databind
jackson-dataformat-xml
jackson-datatype-jsr310
Migrazione da Jackson a azure-json
Le librerie client di Azure per Java sono in fase di migrazione ad azure-json, che non dipende da alcun componente di terze parti e offre primitive, astrazioni e helper condivisi per JSON.
Gli ambienti come Apache Spark, Apache Flink e Databricks potrebbero portare versioni precedenti di azure-core
che non dipendono ancora da azure-json
. Di conseguenza, quando si usano versioni più recenti delle librerie di Azure in tali ambienti, è possibile che si verifichino errori simili a java.lang.NoClassDefFoundError: com/azure/json/JsonSerializable
. È possibile attenuare questo errore aggiungendo una dipendenza esplicita da azure-json
.
Passaggi successivi
Ora che si ha familiarità con i conflitti di versione delle dipendenze e come risolverli, vedere Gestione delle dipendenze per Java per informazioni sul modo migliore per impedirli.