C'è comunque da escludere gli artefatti ereditati da un POM genitore?


119

Gli artefatti dalle dipendenze possono essere esclusi dichiarando un <exclusions>elemento all'interno di un <dependency>Ma in questo caso è necessario escludere un artefatto ereditato da un progetto padre. Segue un estratto del POM in discussione:

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>test</groupId>
  <artifactId>jruby</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
    </parent>

    <dependencies>      
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>ALL-DEPS</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</project>

baseartefatto, dipende javax.mail:mail-1.4.jare ALL-DEPSdipende da un'altra versione della stessa libreria. A causa del fatto che mail.jarda ALL-DEPSesiste nell'ambiente di esecuzione, sebbene non esportato, entra in collisione con mail.jarquello esistente nel genitore, che ha come ambito compile.

Una soluzione potrebbe essere quella di eliminare mail.jar dal POM genitore, ma la maggior parte dei progetti che ereditano base, ne hanno bisogno (così come una dipendenza transtiva per log4j). Quindi quello che vorrei fare è semplicemente escludere la libreria del genitore dal progetto figlio , come si potrebbe fare se basefosse una dipendenza e non il genitore pom:

...
    <dependency>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
        <type>pom<type>
        <exclusions>
          <exclusion>
             <groupId>javax.mail</groupId>
             <artifactId>mail</artifactId>
          </exclusion>
        </exclusions>
    </dependency>
...

Risposte:


49

Qualche idea:

  1. Forse potresti semplicemente non ereditare dal genitore in quel caso (e dichiarare una dipendenza da basecon l'esclusione). Non utile se hai molte cose nel pom genitore.

  2. Un'altra cosa da testare sarebbe dichiarare l' mailartefatto con la versione richiesta da ALL-DEPSsotto dependencyManagementnel pom genitore per forzare la convergenza (anche se non sono sicuro che questo risolverà il problema dello scoping).

<dependencyManagement>
  <dependencies>
    <dependency>    
      <groupId>javax.mail</groupId>
      <artifactId>mail</artifactId>
      <version>???</version><!-- put the "right" version here -->
    </dependency>
  </dependencies>
</dependencyManagement>
  1. Oppure potresti escludere la maildipendenza da log4j se non stai utilizzando le funzionalità che si basano su di esso (ed è quello che farei io):
<dependency>
  <groupId>log4j</groupId>
  <artifactId>log4j</artifactId>
  <version>1.2.15</version>
  <scope>provided</scope>
  <exclusions>
    <exclusion>
      <groupId>javax.mail</groupId>
      <artifactId>mail</artifactId>
    </exclusion>
    <exclusion>
      <groupId>javax.jms</groupId>
      <artifactId>jms</artifactId>
    </exclusion>
    <exclusion>
      <groupId>com.sun.jdmk</groupId>
      <artifactId>jmxtools</artifactId>
    </exclusion>
    <exclusion>
      <groupId>com.sun.jmx</groupId>
      <artifactId>jmxri</artifactId>
    </exclusion>
  </exclusions>
</dependency>
  1. Oppure potresti tornare alla versione 1.2.14 di log4j invece della versione eretica 1.2.15 (perché non hanno contrassegnato le dipendenze di cui sopra come opzionali ?!).

Grazie per la tua risposta. Contiene molte informazioni utili. Riguardo a 1) Come avrai notato non sarà ottimale perché il genitore pom non contiene solo dipendenze che verranno risolte in modo transitorio se la base è stata contrassegnata come dipendenza, ma anche report comuni, gestione delle fonti e altre cose riutilizzate in ogni progetto dell'azienda. In relazione a 2) l'ho provato, ma anche specificando l'ambito del manufatto come fornito, e ha funzionato :). All'inizio, pensavo che poiché la compilazione ha la precedenza su quella fornita, non funzionerà, ma fortunatamente mi sbagliavo (il POM figlio sovrascrive la configurazione del genitore)
Miguel

29

Puoi raggruppare le tue dipendenze all'interno di un progetto diverso con il packaging pomcome descritto dalle best practice di Sonatypes :

<project>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>base-dependencies</artifactId>
    <groupId>es.uniovi.innova</groupId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <dependencies>
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>1.4</version>
        </dependency>
    </dependencies>
</project>

e riferiscili dal tuo genitore-pom (guarda la dipendenza <type>pom</type>):

<project>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>base</artifactId>
    <groupId>es.uniovi.innova</groupId>
    <version>1.0.0</version>
    <packaging>pom</packaging>
    <dependencies>
        <dependency>
            <artifactId>base-dependencies</artifactId>
            <groupId>es.uniovi.innova</groupId>
            <version>1.0.0</version>
            <type>pom</type>
        </dependency>
    </dependencies>
</project>

Il tuo progetto figlio eredita questo genitore-pom come prima. Ma ora, la dipendenza dalla posta può essere esclusa nel progetto figlio all'interno del dependencyManagementblocco:

<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>test</groupId>
    <artifactId>jruby</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <artifactId>base-dependencies</artifactId>
                <groupId>es.uniovi.innova</groupId>
                <version>1.0.0</version>
                <exclusions>
                    <exclusion>
                        <groupId>javax.mail</groupId>
                        <artifactId>mail</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

4
+1 per questo, sebbene il pom figlio dovrebbe usare la sezione <dependencies> piuttosto che <dependencyManagement> poiché quest'ultima è per la gestione delle versioni delle dipendenze dall'interno di un pom genitore.
Matthew Wise

1
Questo è anche noto come BOM, ovvero distinta base :-)
Pim Hazebroek

Funziona solo con le dipendenze transitive? Il mio genitore pom contiene log4j e impedisce il logback del mio pom di funzionare correttamente.
Sridhar Sarnobat

10

Non usare un genitore pom

Potrebbe sembrare estremo, ma allo stesso modo "l'inferno dell'ereditarietà" è una ragione per cui alcune persone voltano le spalle alla programmazione orientata agli oggetti (o preferiscono la composizione all'ereditarietà ), rimuovono il <parent>blocco problematico e copia e incolla tutto ciò di cui <dependencies>hai bisogno (se il tuo team ti dà questa libertà).

Il presupposto che la divisione dei poms in un genitore e un figlio per il "riutilizzo" e "evitare la ridunanza" dovrebbe essere ignorata e dovresti soddisfare prima i tuoi bisogni immediati (la cura è peggiore della malattia). Inoltre, la ridondanza ha i suoi vantaggi, vale a dire l'indipendenza dai cambiamenti esterni (cioè la stabilità).

Questo è più facile di quanto sembri se generi il pom efficace (eclipse lo fornisce ma puoi generarlo dalla riga di comando con mvn help:effective).

Esempio

Voglio usare logbackcome associazione slf4j, ma il mio genitore pom include la log4jdipendenza. Non voglio andare e devo spingere la dipendenza degli altri bambini da log4j nei loro pom.xmlfile in modo che il mio non sia ostruito.


1
Documenta questo (e la procedura che hai seguito) con molta attenzione poiché i futuri manutentori devono rifarlo se hanno bisogno di usare una versione aggiornata del pom genitore.
Thorbjørn Ravn Andersen

Sicuramente intendi non usare le dipendenze nel genitore pom? Parent pom è ancora molto utile per gestire le versioni delle dipendenze e i plugin comuni. Il solo utilizzo per iniettare la dipendenza può ritorcersi contro - lo usiamo solo per le dipendenze indispensabili (come un set di antipasti di avvio primaverili essenziali per un genitore per i microservizi)
Amit Goldstein

No, non sto dicendo di usare un pom genitore leggero. Altri nella tua squadra non ti permetteranno di tagliare il pom genitore perché altre app dipendono dalla spazzatura nel pom genitore che non vuoi.
Sridhar Sarnobat

8

Ridefinisci la dipendenza (nel bambino pom) con il scopesistema che punta a un barattolo vuoto:

<dependency>
    <groupId>dependency.coming</groupId>
    <artifactId>from.parent</artifactId>
    <version>0</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/empty.jar</systemPath>
</dependency>

Il jar può contenere solo un singolo file vuoto:

touch empty.txt
jar cvf empty.txt

Grazie per la tua risposta, su Windows 10 ho dovuto eseguire notepad empty.classquindi jar cvf empty.jar empty.classper generare un jar vuoto.
Rov

1
Sembra una cattiva pratica
Piotr Żak

6

Hai provato a dichiarare esplicitamente la versione di mail.jar che desideri? La risoluzione delle dipendenze di Maven dovrebbe usarla per la risoluzione delle dipendenze su tutte le altre versioni.

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>test</groupId>
  <artifactId>jruby</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <parent>
        <artifactId>base</artifactId>
        <groupId>es.uniovi.innova</groupId>
        <version>1.0.0</version>
    </parent>
    <dependencies>          
        <dependency>
            <groupId>javax.mail</groupId>
            <artifactId>mail</artifactId>
            <version>VERSION-#</version>
            <scope>provided</scope>
        </dependency> 
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>ALL-DEPS</artifactId>
            <version>1.0</version>
            <scope>provided</scope>
            <type>pom</type>
        </dependency>
    </dependencies>
</project>

1
Il tuo approccio, poiché anche la soluzione alternativa n. 2 di Pascal è valida, infatti, hai anche preso in considerazione che la dipendenza dalla posta dovrebbe essere dichiarata come fornita. Grazie.
Miguel

L'ambito fornito non ha funzionato per me. Ho usato il test dell'ambito, controlla la mia risposta. stackoverflow.com/a/55970293/4587961
Yan Khonski

3

La cosa migliore è rendere intransitive le dipendenze che non si desidera ereditare sempre.

Puoi farlo contrassegnandoli nel pom genitore con l'ambito fornito.

Se vuoi ancora che il genitore gestisca le versioni di questi DEP, puoi utilizzare il <dependencyManagement>tag per impostare le versioni che desideri senza ereditarle esplicitamente o passare tale eredità ai figli.


1

Quando chiami un pacchetto ma non vuoi alcune delle sue dipendenze puoi fare una cosa del genere (in questo caso non volevo che il vecchio log4j fosse aggiunto perché avevo bisogno di usare quello più recente):

<dependency>
  <groupId>package</groupId>
  <artifactId>package-pk</artifactId>
  <version>${package-pk.version}</version>

  <exclusions>
    <exclusion>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
    </exclusion>
    <exclusion>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
    </exclusion>
  </exclusions>
</dependency>

<!-- LOG4J -->
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-core</artifactId>
  <version>2.5</version>
</dependency>
<dependency>
  <groupId>org.apache.logging.log4j</groupId>
  <artifactId>log4j-api</artifactId>
  <version>2.5</version>
</dependency>

Questo funziona per me ... ma sono abbastanza nuovo per java / maven quindi forse non è ottimale.


Benvenuto in Stack Overflow e non lasciare che le persone maleducate che ho costantemente respingere le mie domande ti scoraggino dal postare.
Sridhar Sarnobat

1

Avevo davvero bisogno di fare questa cosa sporca ... Ecco come

Ho ridefinito quelle dipendenze con l'ambito test. Scope providednon ha funzionato per me.

Usiamo il plugin Spring Boot per creare fat jar. Abbiamo un modulo comune che definisce le librerie comuni, ad esempio Springfox swagger-2. Il mio super-servizio deve avere un genitore comune (non vuole farlo, ma le regole dell'azienda sono in vigore!)

Quindi il mio genitore o commons ha pom.

<dependencyManagement>

    <!- I do not need Springfox in one child but in others ->

    <dependencies>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>com.google.guava</groupId>
                    <artifactId>guava</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-bean-validators</artifactId>
            <version>${swagger.version}</version>
        </dependency>

       <!- All services need them ->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>${junit.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>${apache.poi.version}</version>
        </dependency>
    </dependencies>
</dependencyManagement>

E il mio super servizio pom.

<name>super-service</name>
<parent>
    <groupId>com.company</groupId>
    <artifactId>common</artifactId>
    <version>1</version>
</parent>

<dependencies>

    <!- I don't need them ->

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-bean-validators</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-core</artifactId>
        <version>2.8.0</version>
        <scope>test</scope>
    </dependency>

    <!- Required dependencies ->

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
     <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
    </dependency>
</dependencies>

Questa è la dimensione del manufatto grasso finale

82.3 MB (86,351,753 bytes) - redefined dependency with scope test
86.1 MB (90,335,466 bytes) - redefined dependency with scope provided
86.1 MB (90,335,489 bytes) - without exclusion

Vale la pena menzionare anche questa risposta: volevo farlo, ma sono pigro ... https://stackoverflow.com/a/48103554/4587961


0

Possiamo aggiungere il genitore pom come dipendenza con il tipo pom e fare l'esclusione su quello. Perché comunque genitore pom viene scaricato. Questo ha funzionato per me

<dependency>
  <groupId>com.abc.boot</groupId>
  <artifactId>abc-boot-starter-parent</artifactId>
  <version>2.1.5.RELEASE</version>
  <type>pom</type>
  <exclusions>
    <exclusion>
      <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
    </exclusion>
  </exclusions>   
</dependency>
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.