Czy w ogóle można wykluczyć artefakty odziedziczone po macierzystym POM?

119

Artefakty z zależności można wykluczyć, deklarując <exclusions>element wewnątrz elementu a <dependency>Ale w tym przypadku konieczne jest wykluczenie artefaktu odziedziczonego z projektu nadrzędnego. Oto fragment omawianego POM:

<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>

baseartefakt, zależy javax.mail:mail-1.4.jari ALL-DEPSzależy od innej wersji tej samej biblioteki. Ze względu na fakt, że mail.jarfrom ALL-DEPSistnieje w środowisku wykonawczym, chociaż nie jest eksportowany, koliduje z tym, mail.jarktóry istnieje w rodzicu, który ma zasięg jako compile.

Rozwiązaniem mogłoby być usunięcie mail.jar z macierzystego POM, ale większość projektów, które dziedziczą base, tego potrzebuje (podobnie jak zależność transtive dla log4j). Więc chciałbym po prostu wykluczyć bibliotekę rodzica z projektu podrzędnego , tak jak można by to zrobić, gdyby basebyła zależnością, a nie pom-rodzicem:

...
    <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>
...
Miguel
źródło

Odpowiedzi:

49

Jakieś pomysły:

  1. Może w takim przypadku nie mógłbyś po prostu dziedziczyć po rodzicu (i zadeklarować zależność od basez wykluczeniem). Nie jest przydatne, jeśli masz dużo rzeczy w pompie rodzica.

  2. Kolejną rzeczą do testu byłoby stwierdzenie, że mailartefakt z wersją wymaganych ALL-DEPSw ramach dependencyManagementw pom nadrzędnego celu zmuszenia konwergencji (chociaż nie jestem pewien, że to rozwiąże problem określania zakresu).

<dependencyManagement>
  <dependencies>
    <dependency>    
      <groupId>javax.mail</groupId>
      <artifactId>mail</artifactId>
      <version>???</version><!-- put the "right" version here -->
    </dependency>
  </dependencies>
</dependencyManagement>
  1. Lub możesz wykluczyć mailzależność z log4j, jeśli nie korzystasz z funkcji, na których jest on oparty (i tak bym zrobił):
<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. Albo możesz powrócić do wersji 1.2.14 log4j zamiast heretyckiej wersji 1.2.15 (dlaczego nie oznaczyli powyższych zależności jako opcjonalne ?!).
Pascal Thivent
źródło
Dzięki za odpowiedź. Zawiera wiele przydatnych informacji. Odnośnie 1) Jak zauważyłeś, nie będzie optymalny, ponieważ macierzysty pom nie tylko zawiera zależności, które przejściowo zostaną rozwiązane, jeśli baza została oznaczona jako zależność, ale także wspólne raportowanie, zarządzanie źródłami i inne rzeczy ponownie wykorzystywane w każdym projekcie w firmie. W odniesieniu do 2) próbowałem, ale również określając zakres artefaktu zgodnie z dostarczeniem i zadziałało :). Na początku myślałem, że ma pierwszeństwo przed kompilacji pod warunkiem, że nie będzie działać, ale na szczęście myliłem (konfiguracja podrzędnego POM nadpisuje rodzica)
Miguel
29

Możesz zgrupować swoje zależności w ramach innego projektu z pakietowaniem, pomzgodnie z opisem w Sonatypes Best Practices :

<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>

i odwołaj się do nich od swojego rodzica-pom (obserwuj zależność <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>

Twój projekt podrzędny dziedziczy tę pompę rodzicielską jak poprzednio. Ale teraz zależność od poczty można wykluczyć w projekcie potomnym w dependencyManagementbloku:

<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>
timomeinen
źródło
4
+1 do tego, chociaż pom-dziecko powinno używać sekcji <dependency> zamiast <dependencyManagement>, ponieważ ta ostatnia służy do zarządzania wersjami zależności z macierzystego pom.
Matthew Wise,
1
Jest to również znane jako BOM, czyli zestawienie materiałów :-)
Pim Hazebroek
Czy to działa tylko z zależnościami przechodnimi? Mój rodzic pom zawiera log4j i uniemożliwia poprawne działanie logbacka mojego pom.
Sridhar Sarnobat
10

Nie używaj pompki dla rodziców

To może brzmieć ekstremalnie, ale w ten sam sposób, w jaki "piekło dziedziczenia" jest powodem, dla którego niektórzy ludzie odwracają się od programowania obiektowego (lub wolą kompozycję niż dziedziczenie ), usuń problematyczny <parent>blok i skopiuj i wklej wszystko, <dependencies>czego potrzebujesz (jeśli twój zespół daje ci tej wolności).

Założenie, że dzielenie poms na rodzica i dziecko w celu „ponownego użycia” i „uniknięcia zbędności” powinno zostać zignorowane i należy najpierw zaspokoić swoje najpilniejsze potrzeby (lekarstwo jest gorsze niż choroba). Poza tym redundancja ma swoje zalety - mianowicie niezależność od zmian zewnętrznych (czyli stabilność).

Jest to łatwiejsze niż się wydaje, jeśli wygenerujesz skuteczny pom (zapewnia go eclipse, ale możesz wygenerować go z wiersza poleceń za pomocą mvn help:effective).

Przykład

Chcę użyć logbackjako wiązania slf4j, ale mój macierzysty pom zawiera log4jzależność. Nie chcę iść i muszę wpychać zależność innych dzieci od log4j do ich własnych pom.xmlplików, aby mój był niezakłócony.

Sridhar Sarnobat
źródło
1
Udokumentuj to (i procedurę, którą zastosowałeś) bardzo uważnie, ponieważ przyszli opiekunowie będą musieli to powtórzyć, jeśli będą potrzebować zaktualizowanej wersji macierzystego pliku pom.
Thorbjørn Ravn Andersen
Z pewnością masz na myśli, że nie używaj zależności w pom-rodzicu? Nadrzędny pom jest nadal bardzo przydatny do zarządzania wersjami zależności i typowymi wtyczkami. Samo użycie go do wstrzyknięcia zależności może przynieść odwrotny skutek - używamy go tylko do niezbędnych zależności (takich jak zestaw podstawowych starterów rozruchowych dla rodzica dla mikrousług)
Amit Goldstein
Nie, nie mówię, używaj lekkiego pomponu dla rodziców. Inni w twoim zespole nie pozwolą ci przyciąć nadrzędnego pom, ponieważ inne aplikacje zależą od śmieci w nadrzędnym pomie, których nie chcesz.
Sridhar Sarnobat
8

Przedefiniuj zależność (w pom dziecku) z scopesystemem wskazującym na pusty słoik:

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

Jar może zawierać tylko jeden pusty plik:

touch empty.txt
jar cvf empty.txt
Bax
źródło
Dziękuję za odpowiedź, na Windows 10 musiałem notepad empty.classwtedy uruchomić, jar cvf empty.jar empty.classaby wygenerować pusty słoik.
Rov
1
Wygląda na zły trening
Piotr Żak
6

Czy próbowałeś jawnie zadeklarować żądaną wersję mail.jar? Rozwiązanie zależności Maven powinno używać tego do rozpoznawania zależności w przypadku wszystkich innych wersji.

<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>
porterhouse91
źródło
1
Twoje podejście, ponieważ obejście nr 2 Pascala jest również poprawne, w rzeczywistości wziąłeś również pod uwagę, że zależność od poczty powinna być zadeklarowana jako dostarczona. Dziękuję Ci.
Miguel
Podany zakres nie zadziałał dla mnie. Użyłem testu lunety, sprawdź moją odpowiedź. stackoverflow.com/a/55970293/4587961
Yan Khonski
3

Najlepiej jest uczynić zależności, które nie zawsze chcesz dziedziczyć, nieprzechodnie.

Możesz to zrobić, zaznaczając je w pompie nadrzędnym z podanym zakresem.

Jeśli nadal chcesz, aby rodzic zarządzał wersjami tych dep, możesz użyć <dependencyManagement>tagu, aby ustawić żądane wersje bez jawnego ich dziedziczenia lub przekazywania tego dziedziczenia dzieciom.

Ajax
źródło
1

Kiedy wywołujesz pakiet, ale nie chcesz niektórych jego zależności, możesz zrobić coś takiego (w tym przypadku nie chciałem dodać starego log4j, ponieważ musiałem użyć nowszego):

<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>

To działa dla mnie ... ale java / maven jestem całkiem nowy, więc może nie jest optymalny.

bastienb1991
źródło
Witaj w Stack Overflow i nie pozwól, aby niegrzeczni ludzie, których ciągle odrzucałem, zniechęcali Cię do publikowania postów.
Sridhar Sarnobat
1

Naprawdę musiałem zrobić tę brudną rzecz ... Oto jak

Przedefiniowałem te zależności z zakresem test. Zakresprovided nie działa dla mnie.

Używamy wtyczki Spring Boot do budowy słoika. Mamy wspólny moduł, który definiuje wspólne biblioteki, na przykład Springfox swagger-2. Moja super usługa musi mieć wspólnego rodzica (nie chce tego robić, ale zasady firmy obowiązują!)

Więc mój rodzic lub wspólnoty mają 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>

I mój super-serwisowy 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>

To jest rozmiar ostatniego artefaktu tłuszczu

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

Warto też wspomnieć o tej odpowiedzi - chciałem to zrobić, ale jestem leniwy ... https://stackoverflow.com/a/48103554/4587961

Yan Khonski
źródło
0

Możemy dodać rodzica pom jako zależność z typem pom i zrobić z tego wykluczenie. Bo tak czy owak pobierany jest macierzysty pom. To zadziałało dla mnie

<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>
Viki Jain
źródło