Zapanuj nad swoim plikiem pom.xml
Mechanizm zależności jest kluczową cechą Maven. Daje on dużą elastyczność. Dzięki Wersji 2.0 i wprowadzeniu Transitive Dependencies (Zależności Tranzytywne) zarządzanie zależnościami stało się prostsze. Mimo tego możemy zarządzać zależnościami na dwa sposoby: szybko/łatwo lub właściwie. W tym artykule pokażę Ci ten drugi sposób.
Czym jest POM?
POM (Project Object Model) to plik XML, który zawiera konfigurację projektu używaną przez Maven do jego budowy. Minimalny POM powinien zawierać tagi opisujące projekt: projekt root, modelVersion, groupId, artifactId i version. Te trzy ostatnie wartości tworzą nazwę artefaktu projektu w formacie ::.
Jedną z konfiguracji, które można zawrzeć w POM, są zależności projektu oparte na artefaktach. W naszych projektach korzystamy z nich. Używamy do tego sekcji w pliku POM.
Co daje organizacja zależności?
Zależności można dodać do projektu na kilka różnych sposobów. Podejścia można ze sobą łączyć. Efektem tego może być utrata kontroli czego efektem będą mieszane wersje artefaktów, które często są niekompatybilnymi ze sobą. Prowadzi to do sytuacji, w której niektóre funkcje przestają działać z powodu niekompatybilnych klas itd. W następnych sekcjach opiszemy lepsze sposoby na zorganizowanie tego chaosu.
Zarządzanie zależnościami
Pliki POM można zorganizować w strukturę. W tym przypadku jeden plik POM może dziedziczyć konfigurację od swojego nadrzędnego POM. Jeden nadrzędny POM może mieć wiele podrzędnych POM. Każdy podrzędny POM powinien mieć zadeklarowanego rodzica lub nadrzędny POM powinien również zawierać listę podrzędnych. Każdy podrzędny POM może także mieć swoje własne podrzędne, a każde z nich będzie dziedziczyć konfigurację nie tylko od bezpośredniego rodzica, ale także od jego rodzica itd. Ponieważ mamy specjalną sekcję, ten mechanizm dziedziczenia może być używany do zarządzania zależnościami. Wszystko, co musimy zrobić, to zadeklarować tę sekcję w naszym nadrzędnym POM i określić wszystkie zależności, których chcemy używać w całym projekcie, np.:
<dependencymanagement>
<dependencies>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-core</artifactid>
<version>5.2.2.RELEASE</version>
<scope>compile</scope>
</dependency>
</dependencies>
</dependencymanagement>
w naszych podrzędnych plikach POM deklarujemy tylko, że chcemy użyć tej zależności:
<dependencies>
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-core</artifactid>
</dependency>
</dependencies>
bez określania wersji lub zakresu. Pozwala to na łatwe zarządzanie wersjami artefaktów, ponieważ musimy sprawdzić tylko jeden plik, aby zmienić wersję w całym projekcie.
Nadrzędny POM
Zamiast tworzyć własny nadrzędny POM z sekcją, możemy użyć istniejącego. Dobrym przykładem jest tutaj Spring Boot. Aby go użyć, wystarczy zadeklarować rodzica:
<parent>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-parent</artifactid>
<version>2.2.2.RELEASE</version>
</parent>
W ten sposób, oprócz zależności otrzymujemy także współdzieloną konfigurację wtyczek. Oczywiście, możemy to zrobić ręcznie. Używając do tego pluginManagement w podobny sposób, jak używaliśmy dependencyManagement, ale zamiast tego możemy skorzystać z gotowego rozwiązania.
BOM
BOM (Bill Of Materials) to specjalny rodzaj POM i również kolejny sposób na konfigurację zależności. Przykładowo kiedy nie możemy użyć nadrzędnych POM. Aby dodać go do projektu, można ustawić go jako nadrzędny POM. Można go również zaimportować w sekcji dependencyManagement:
<dependencymanagement>
<dependencies>
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-dependencies</artifactid>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencymanagement>
Czyszczenie
Teraz gdy masz wiedzę, jak zarządzać zależnościami dowiesz się, jak oczyścić Twój projekt.
Właściwości
Jeśli używasz podobnych zależności w tej samej wersji, np.:
<dependency>
<groupid>org.apache.activemq</groupid>
<artifactid>activemq-broker</artifactid>
<version>5.15.11</version>
</dependency>
<dependency>
<groupid>org.apache.activemq</groupid>
<artifactid>activemq-camel</artifactid>
<version>5.15.11</version>
</dependency>
możesz użyć
<dependency>
<groupid>org.apache.activemq</groupid>
<artifactid>activemq-broker</artifactid>
<version>${activemq.version}</version>
</dependency>
<dependency>
<groupid>org.apache.activemq</groupid>
<artifactid>activemq-camel</artifactid>
<version>${activemq.version}</version>
</dependency>
i zadeklarować wersję we właściwościach:
<properties>
<activemq.version>5.15.11</activemq.version>
</properties>
Takie podejście pozwoli Ci uniknąć sytuacji, w której zapomnisz o zmianie wersji podczas aktualizacji.
Duplikacje zależności
Jeśli masz wiele POM-ów w projekcie łatwo w takiej sytuacji o duplikaty deklaracji. W celu szybkiego ich odnalezienia możesz użyć maven-enforcer-plugin. Po prostu dodaj:
<build>
<plugins>
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-enforcer-plugin</artifactid>
<version>3.0.0-M3</version>
<executions>
<execution>
<id>no-duplicate-declared-dependencies</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<banduplicatepomdependencyversions>
</banduplicatepomdependencyversions></rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
w swoim projekcie masz duplikaty, to po każdym zbudowaniu projektu zobaczysz ostrzeżenia:
[WARNING] Some problems were encountered while building the effective model for pl.j-labs:spring-boot-demo:jar:1.0.0-SNAPSHOT
[WARNING] 'dependencyManagement.dependencies.dependency.(groupId:artifactId:type:classifier)' must be unique: org.springframework.boot:spring-boot-starter:jar -> duplicate declaration of version 2.2.2.RELEASE @ line 33, column 29
[WARNING]
[WARNING] It is highly recommended to fix these problems because they threaten the stability of your build.
[WARNING]
[WARNING] For this reason, future Maven versions might no longer support building such malformed projects.
Zależności tranzytywne
Kolejny dobry przykład, dlaczego warto używać maven-enforcer-plugin, to eliminacja zależności tranzytywnych z Twojego projektu. Deklarowanie zależności bezpośrednio może być użyteczne, ponieważ upraszcza wykrywanie konfliktów lub sprawia, że mamy pewność, że używamy dokładnie tego, co chcemy.
Aby uzyskać listę zależności niezadeklarowanych bezpośrednio, należy dodać kolejną regułę do konfiguracji wtyczki:
<rules>
<bantransitivedependencies>
[...]
</bantransitivedependencies></rules>
W takim przypadku budowa projektu zakończy się niepowodzeniem, jeśli wtyczka wykryje zależności tranzytywne:
[WARNING] Rule 0: org.apache.maven.plugins.enforcer.BanTransitiveDependencies failed with message:
Rule 0: org.apache.maven.plugins.enforcer.BanTransitiveDependencies failed with message:
pl.j-labs:spring-boot-demo:jar:1.0.0-SNAPSHOT
org.springframework.boot:spring-boot-starter-web:jar:2.2.2.RELEASE:compile has transitive dependencies:
org.springframework.boot:spring-boot-starter:jar:2.2.2.RELEASE:compile
org.springframework.boot:spring-boot:jar:2.2.2.RELEASE:compile
org.springframework.boot:spring-boot-autoconfigure:jar:2.2.2.RELEASE:compile
org.springframework.boot:spring-boot-starter-logging:jar:2.2.2.RELEASE:compile
ch.qos.logback:logback-classic:jar:1.2.3:compile
ch.qos.logback:logback-core:jar:1.2.3:compile
org.apache.logging.log4j:log4j-to-slf4j:jar:2.12.1:compile
org.apache.logging.log4j:log4j-api:jar:2.12.1:compile
org.slf4j:jul-to-slf4j:jar:1.7.29:compile
jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
org.yaml:snakeyaml:jar:1.25:runtime
org.springframework.boot:spring-boot-starter-json:jar:2.2.2.RELEASE:compile
[...]
Oprócz dwóch funkcji, które opisałem, ta wtyczka ma wiele innych zastosowań. Pełną listę funkcji znajdziesz na jej stronie.
Podsumowanie
Czasami komplikacje wynikające z bałaganu w plikach POM mogą być bardzo bolesne. Czysty POM nie tylko pozwoli ich uniknąć, ale także przyspieszy przyszły rozwój aplikacji.