Per quali problemi sono le esclusioni di dipendenza soluzioni sensate?

4

Ho visto alcuni strumenti di costruzione nella mia carriera, e hanno tutti avuto i loro capricci. Sto solo ora esaminando Maven e ho scoperto l'idea di "esclusioni di dipendenza" per la prima volta. Onestamente non capisco quale problema legittimo questo meccanismo sta cercando di risolvere.

Prendiamo il POM di Apache Pig, ad esempio:

<project xmlns="http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.apache.pig</groupId>
    <artifactId>pig</artifactId>
    <packaging>jar</packaging>
    <version>0.16.0.2.5.0.0-1245</version>
    <dependencies>
        <!-- ... -->
        <dependency>
            <groupId>org.apache.avro</groupId>
            <artifactId>avro</artifactId>
            <version>1.7.4</version>
            <exclusions>
                <exclusion>
                    <!--  Don't pull in Avro's (later) version of Jetty. -->
                    <groupId>org.mortbay.jetty</groupId>
                    <artifactId>jetty</artifactId>
                </exclusion>
                <exclusion>
                    <!--
                     Exclude Avro's version of ant since it conflicts with Jetty's.
                    -->
                    <groupId>org.apache.ant</groupId>
                    <artifactId>ant</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- ... -->
    </dependencies>
    <!-- ... -->
</project>

Questo particolare progetto dipende dal avro artefatto dal gruppo org.apache.avro , ma rimuove due dipendenze da quella dipendenza (vale a dire jetty e ant ) perché "in conflitto". Apparentemente, è perfettamente corretto rimuovere queste dipendenze da avro , come faranno quelle incluse da avro .

Per me, questo sembra chiedere dei guai, e penserei che qualsiasi altra risoluzione dei conflitti di dipendenza sarebbe preferibile a questo.

Interagendo con le dipendenze di una dipendenza, il progetto Pig sta effettivamente rattoppando il progetto Avro, e in modo abbastanza cieco, poiché il vincolo della versione su Avro non identifica strettamente una versione del progetto.

Non avrebbe più senso rifiutare del tutto la build o costruire in due versioni separate di ant e jetty nel progetto (supponendo che possano essere mantenute segregate nel sistema di caricamento classi di Java)?

    
posta Rhymoid 22.11.2016 - 18:39
fonte

2 risposte

2

Ci sono due problemi che risolve, ed entrambi hanno un aggiramento, ma né il work-around è sempre disponibile per i progetti del mondo reale.

Ma prima, per coloro che non hanno familiarità con il meccanismo delle dipendenze di Maven :

  1. Maven ti consente di specificare le versioni specifiche delle dipendenze dell'applicazione nel POM (descrittore del progetto).
  2. Le tue dipendenze possono avere delle dipendenze, nel qual caso il progetto includerà quelle dipendenze (fondamentale quando si costruisce un WAR, perché altrimenti non verrà eseguito).
  3. Mentre lavori nel grafico delle dipendenze, potresti trovare più dipendenze per lo stesso artefatto con versioni differenti.
  4. Maven ha un approccio un po 'falso per risolvere quei conflitti, in quanto la versione più vicina alla radice dell'albero vince. Se ci sono più versioni alla stessa profondità dell'albero, la risoluzione è arbitraria: "la prima definizione vince" ma non ci sono regole per le quali è il primo.

A volte questo meccanismo può darti dipendenze che non vuoi, quindi utilizzi la regola exclusion per impedire a Maven di includere specifiche dipendenze transitive.

Quindi, ai problemi.

Problema 1: la dipendenza indesiderata

Supponiamo che tu stia creando un'app Web utilizzando Spring 3.x e inoltre stai utilizzando SLF4J con l'adattatore Log4J. Tuttavia, Spring 3.x utilizza internamente il Commons-Logging. Nessun problema, SLF4J include anche un adattatore per sostituire l'implementazione di Commons-Logging.

Tuttavia, a seconda dell'ordine specifico in cui Maven memorizza le dipendenze transitive nel WAR, è possibile che i messaggi di registrazione Spring stiano effettivamente passando attraverso l'implementazione di Commons-Logging. Aggiungendo un'esclusione alle dipendenze Spring rilevanti, si impedisce che ciò accada.

Direi che il modo corretto per risolvere questo problema è che Spring contrassegni la sua dipendenza come provided . Ciò impedirà a Maven di includerlo automaticamente nella WAR generata, partendo dal presupposto che il progetto principale o l'ambiente di esecuzione fornisce la dipendenza. Sfortunatamente, probabilmente non sarai in grado di convincere gli autori delle dipendenze a farlo (e in molti casi è non l'approccio corretto), quindi aggiungi le esclusioni dove necessario.

Problema # 2: dipendenze transitive incompatibili

Questo è il caso che sembri avere: la versione di jetty che dipende da avro non è retrocompatibile con il resto del progetto. Questo è generalmente infrequente per gli artefatti Java ben utilizzati, perché la compatibilità con le versioni precedenti è una cosa positiva nella comunità Java. Ma a volte le correzioni di bug lo rendono impossibile.

Questo è un caso in cui l'autore dell'applicazione (cioè tu) ha bisogno di intervenire e assicurarsi che tutte le dipendenze giochino bene. Se il tuo progetto richiede una versione precedente di jetty , allora devi usare una versione di avro che supporti quella versione, o molto, molto meglio aggiornare qualunque cosa sia nell'applicazione che dipende da il vecchio comportamento.

Una brutta soluzione consiste nel specificare esplicitamente le versioni di dipendenza nel progetto, utilizzando una sezione dependency-management o facendo riferimento direttamente alla dipendenza. Entrambe queste violano l'intera premessa delle dipendenze transitive, ma ...

    
risposta data 22.11.2016 - 21:45
fonte
2

Risolve il problema delle librerie di terze parti che impostano le versioni delle dipendenze comuni in modo più ristretto del necessario. Ad esempio, potrebbero effettivamente funzionare con qualcosa di 2.0 o superiore, ma impostano i loro requisiti a 2.4+ o, peggio, esattamente a 2.4.3. Perché qualcuno dovrebbe farlo? Potrebbero non avere le risorse per testare le versioni precedenti e non si sentono a proprio agio nel richiedere supporto. Un sacco di volte, semplicemente non ne sanno niente di meglio. Qualunque sia la ragione, non è un evento insolito in natura.

Ovviamente, se hai il controllo su entrambe le librerie, il miglior modo di agire è quello di correggere le dipendenze dall'altro. Se non lo fai, le esclusioni ti consentono di prendere il rischio di test per far funzionare tutto insieme.

E no, più versioni di una libreria non sono fattibili. Anche se i runtime linguistici potrebbero occuparsi delle collisioni nello spazio dei nomi, cosa che non possono fare, avresti ancora problemi se quelle due versioni dovessero effettivamente interagire. Non c'è modo di risolvere i conflitti tra diverse versioni di tipi di dati e codice. L'unico modo per utilizzare due versioni separate di una libreria è se si trovano in due processi separati, come in un'architettura di microservizi.

    
risposta data 22.11.2016 - 21:26
fonte

Leggi altre domande sui tag