Podstawy Maven - Pierwsze kroki z Maven

W tym rozdziale poznamy podstawowe cechy projektów tworzonych w Mavenie.

Dowiemy się m. in.:

  • jak wygląda podstawowa struktura katalogów projektów Maven,
  • jak skonfigurować prosty projekt Maven,
  • czym są fazy podczas budowy projektu oraz do czego służą pluginy Maven,
  • jak zbudować i uruchomić projekt,
  • gdzie Maven przechowuje zależności,
  • jak dodać zależność do naszego projektu,
  • jak zlecić Mavenowi wykonanie testów jednostkowych,
  • jak wygenerować szkielet projektu za pomocą generatora archetypów.

Pierwszy projekt

Na początku naszej przygody z Mavenem zobaczymy na prostym przykładzie jak wygląda struktura projektów Mavena, jak zbudować i uruchomić projekt oraz dodać do niego zależności, a także do czego służy lokalne repozytorium Mavena.

Wstępna struktura projektu

Maven zakłada, że nasze projekty będą miały konkretną strukturę katalogów (konwencja przed konfiguracją):

  • ${basedir}/src/main/java – tu umieszczamy nasz kod Java – jeżeli nasza klasa ma być w pakiecie com.kursjava.maven, to umieścimy ją w katalogu:
    ${basedir}/src/main/java/com/kursjava/maven
  • ${basedir}/src/main/resources – miejsce na zasoby wymagane przez naszą aplikację (pliki konfiguracyjne itp.),
  • ${basedir}/src/test/java – testy jednostkowe,
  • ${basedir}/src/test/resources – zasoby testów.
${basedir} to zmienna w Mavenie, która oznacza główny katalog projektu, w którym zawarte są wszystkie pozostałe pliki i katalogi projektu.

Stwórzmy nasz pierwszy projekt w Maven – będzie on zawierał na razie tylko jedną klasę (bez testów i zasobów), wystarczy więc stworzyć katalogi src/main/java. Cały projekt zawrzemy w nadrzędnym katalogu o nazwie najprostszy-projekt (${basedir} oznacza właśnie ten katalog):

najprostszy-projekt
|
`-- src
    |
    `-- main
        |
        `-- java

Klasa Java w projekcie

Do projektu dodamy jedną klasę o nazwie com.kursjava.maven.HelloMaven, która wypisze na ekran tekst "Hello Maven!":

najprostszy-projekt/src/main/java/com/kursjava/maven/HalloMaven.java
package com.kursjava.maven;

public class HelloMaven {
  public static void main(String[] args) {
    System.out.println("Hello Maven!");
  }
}

Ponieważ nasza klasa jest w pakiecie com.kursjava.maven, musimy utworzyć katalogi odpowiadające pakietowi naszej klasy w przygotowanym wcześniej katalogu najprostszy-projekt/src/main/java.

Podstawowa konfiguracja Maven w pom.xml

Ostatnim elementem w tym projekcie jest plik konfiguracyjny Maven – pom.xml. Należy utworzyć go w katalogu głównym najprostszy-projekt. Treść minimalnego pliku pom.xml naszego projektu jest następująca:

najprostszy-projekt/pom.xml
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.kursjava.maven</groupId>
  <artifactId>hello-maven</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
</project>

Analiza powyższego pliku pom.xml:

  • początek pliku informuje o wersji XML,
  • główny element całej konfiguracji to project, którego atrybuty informują o przestrzeni nazw tagów używanych w tym pliku XML oraz o lokalizacji schematu XSD, który służy do walidacji formatu plików POM,
  • modelVersion – oznacza wersję pliku POM – powinna być ustawiana na wartość 4.0.0,
  • groupId, artifactId oraz version to elementy identyfikujące nasz projekt:
    • groupId – unikalny identyfikator grupy, do której należy ten projekt – zazwyczaj jest to odwrócona domena autora bądź firmy odpowiedzialnej za projekt, z ewentualnym dodatkiem identyfikującym podgrupę projektów,
    • artifactId – nazwa tego konkretnego projektu,
    • version – aktualna wersja projektu – wykonując zmiany w naszym projekcie i dokonując release'ów powinniśmy tą wersję aktualizować.
  • properties – ustawiamy dwa parametry wpływające na kompilację naszego kodu:
    • maven.compiler.source – źródła mają być traktowane jako kod Java w wersji 1.8,
    • maven.compiler.target – nasze klasy mają być kompilowane do wersji 1.8,
  • dodatkowo, ustawiamy kodowanie czytanych i zapisywanych plików na UTF-8 korzystając z parametru project.build.sourceEncoding.
Starsza wersja pluginu maven-compiler-plugin, który kompiluje nasze klasy (pośrednio lub bezpośrednio korzystając z kompilatora javac), domyślnie zakładała wersję naszego kodu w wersji Java 1.5. W nowszej odsłonie pluginu podniesiono tą wersję do 1.6. Jeżeli korzystamy z kompilatora Java w wersji 12 lub wyższej, to minimalna obsługiwana wersji kodu Java to 1.7. Dlatego korzystamy z parametrów maven.compiler.source i maven.compile.target. Jeżeli byśmy tego nie zrobili, to podczas budowania projektu próba kompilacji zakończyłaby się błedami:
[ERROR] Source option 6 is no longer supported. Use 5 or later. [ERROR] Target option 6 is no longer supported. Use 7 or later.
(ewentualnie zamiast "option 6" zobaczylibyśmy "option 5")

Struktura projektu z klasą i pom.xml

Finalnie struktura naszego projektu wygląda następująco:

najprostszy-projekt
|
|-- pom.xml
|
`-- src
    |
    `-- main
        |
        `-- java
            |
            `-- com
                |
                `-- kursjava
                    |
                    `-- maven
                        |
                        `-- HelloMaven.java

Budujemy projekt

Projekt jest gotowy do zbudowania. Przechodzimy do linii komend i wywołujemy komendę mvn install:

> mvn install [INFO] Scanning for projects... [INFO] [INFO] -------------< com.kursjava.maven:hello-maven >------------ [INFO] Building hello-maven 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-maven --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to D:\kurs_maven\przyklady\najprostszy-projekt\target\classes [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-maven --- [INFO] No sources to compile [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-maven [INFO] No tests to run. [INFO] [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-maven --- [INFO] Building jar: D:\kurs_maven\przyklady\najprostszy-projekt\target\hello-maven-1.0-SNAPSHOT.jar [INFO] [INFO] --- maven-install-plugin:2.4:install (default-install) @ hello-maven [INFO] Installing D:\kurs_maven\przyklady\najprostszy-projekt\target\hello-maven-1.0-SNAPSHOT.jar to C:\Users\Przemek\.m2\repository\com\kursjava\maven\hello-maven\1.0-SNAPSHOT\hello-maven-1.0-SNAPSHOT.jar [INFO] Installing D:\kurs_maven\przyklady\najprostszy-projekt\pom.xml to C:\Users\Przemek\.m2\repository\com\kursjava\maven\hello-maven\1.0-SNAPSHOT\hello-maven-1.0-SNAPSHOT.pom [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.746 s [INFO] Finished at: 2020-03-05T12:27:02+01:00 [INFO] ------------------------------------------------------------------------

Jedna komenda spowodowała szereg akcji, które zostały wykonane przez odpowiednie pluginy. Maven najpierw skompilował klasę HelloMaven za pomocą kompilatora maven-compiler-plugin:

[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-maven --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to D:\kurs_maven\przyklady\najprostszy-projekt\target\classes

Nastepnie, Maven próbował skompilować i uruchomić testy jednostkowe – te prace oddelegował do pluginów maven-compiler-plugin i maven-surefire-plugin. Na razie żadnych testów nie ma w naszym projekcie, więc Maven poinformował nas, że nie ma testów do skompilowania i uruchomienia:

[INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-maven --- [INFO] No sources to compile [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-maven [INFO] No tests to run.

Kolejnym etapem było wygenerowanie pliku JAR z naszą jedyną klasą. Ten JAR nazywamy artefaktem. Za to zadanie odpowiedzialny jest plugin maven-jar-plugin:

[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-maven [INFO] Building jar: D:\kurs_maven\przyklady\najprostszy-projekt\target\hello-maven-1.0-SNAPSHOT.jar

Gdy wszystkie pozostałe fazy zakończyły się sukcesem, Maven wykonał ostatnią z nich – install. W tej fazie plugin maven-install-plugin przekopiował plik JAR, wygenerowany w poprzednim kroku przez plugin maven-jar-plugin, do lokalnego repozytorium .m2/repository:

[INFO] --- maven-install-plugin:2.4:install (default-install) @ hello-maven [INFO] Installing D:\kurs_maven\przyklady\najprostszy-projekt\target\hello-maven-1.0-SNAPSHOT.jar to C:\Users\Przemek\.m2\repository\com\kursjava\mvn\hello-maven\1.0-SNAPSHOT\hello-maven-1.0-SNAPSHOT.jar

Uruchomienie komendy mvn install spowodowało, że wykonanych zostało dużo operacji. Wynika to z faktu, że fazy są od siebie zależne – faza install wymaga pliku JAR generowanego w fazie package. Faza package, z kolei, wymaga przeprowadzenia testów w fazie test, a faza test – skompilowanego w fazie compile kodu.

Wszystkie pliki wygenerowane podczas budowania projektu umieszczane są w katalogu o nazwie target w katalogu głównym projektu. Katalog target zawiera m. in.:

  • skompilowane klasy projektu w podkatalogu classes,
  • listy plików, które brały udział w procesie kompilacji,
  • wygenerowany plik JAR.
Dla użytkowników Gita: ponieważ katalog target zawiera pliki, które są generowane podczas budowania projektów, zazwyczaj katalog target dodawany jest do pliku .gitignore, by Git nie śledził zmian w tym katalogu.

Zajrzyj do katalogu target, by zaznajomić się z jego zawartością. Zauważ, jaką nazwę Maven nadał plikowi JAR:

najprostszy-projekt
|
`-- target
    |
    `-- hello-maven-1.0-SNAPSHOT.jar

Nazwa ta to połączenie wartości elementu artifactId i elementu version z pliku konfiguracyjnego pom.xml:

<groupId>com.kursjava.maven</groupId>
<artifactId>hello-maven</artifactId>
<version>1.0-SNAPSHOT</version>
Jeżeli pierwszy raz korzystasz z Mavena na Twoim komputerze, to po wykonaniu komendy mvn install zobaczysz długą listę komunikatów Mavena dotyczących pobierania pluginów wymaganych przez Maven do pracy:
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plugin-2.6.pom Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/2.6/maven-resources-plugin-2.6.pom (8.1 kB at 11 kB/s) Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-plugins/23/maven-plugins-23.pom (9.2 kB at 70 kB/s)
Te konkretne komunikaty zobaczysz jednorazowo – Maven zapisze w lokalnym repozytorium wymagane przez niego pliki i będzie je od tej pory używał. Zawsze, gdy będziesz korzystał z zależności bądź pluginów, których wcześniej nie używałeś (lub innych wersji tych zależności/pluginów), zostaną one jednorazowo pobrane, więc od czasu do czasu będziesz widywał komunikaty podobne do powyższych.

Efekt mvn install i lokalne repozytorium .m2

Gdy w swoim projekcie korzystasz z pluginów Mavena i dodajesz zależności do różnych bibliotek (takich jak JUnit, Spring, Hibernate itp.), Maven pobierze je automatycznie i umieści w Twoim lokalnym repozytorium artefaktów.

To repozytorium to katalog o nazwie .m2 (kropka m2), znajdujący się domyślnie w katalogu Twojego użytkownika.

Maven, pobierając kolejne pluginy i biblioteki, które wykorzystujesz w swoim projekcie, buduje w ten sposób lokalne repozytorium tych zależności. Mogą one być używane pomiędzy wieloma projektami, dzięki czemu, gdy będziesz chciał z nich skorzystać w kolejnym projekcie, będą gotowe do użycia.

Spójrzmy, jak wygląda lokalne repozytorium artefaktów po niedawnej instalacji Mavena i wykonaniu mvn install na prostym projekcie z tego rozdziału:

Zawartosc lokalnego repozytorium m2

W katalogu .m2 znajduje się podkatalog repository, do którego Maven pobiera zarówno wymagane przez niego pluginy (jak maven-compiler-plugin), jak i zależności naszych projektów. Na razie nasze lokalne repozytorium nie jest zbyt duże, ale będzie się rozrastać, gdy będziemy wymagali do pracy coraz to nowych pluginów i zależności.

Jeżeli zajrzymy do np. .m2\repository\org\apache\maven\plugins\maven-compiler-plugin\3.1, to znajdziemy w nim plik maven-compiler-plugin-3.1.jar, czyli plugin służący do kompilacji klas, z którego korzysta Maven. W katalogu tym znajdziemy także plik o nazwie maven-compiler-plugin-3.1.pom, który jest plikiem POM tego pluginu. Pluginy to także Javowe projekty tworzone z wykorzystaniem Mavena.

Dzięki temu, że w repozytorium artefaktów przechowywane są nie tylko pliki JAR, ale także pliki POM, Maven jest w stanie sprawdzić, jakie zależności mają nasze zależności. Często biblioteki, z których chcemy skorzystać, same mają zależności do innych bibliotek itd. – Maven śledzi te zależności zaglądając do plików POM i pobiera je wszystkie automatycznie. Takie zależności nazywamy przechodnimi (transitive dependencies).

W repozytorium .m2 znajdują się nie tylko pluginy i zależności naszych projektów, ale także nasze projekty w postaci plików JAR. W poprzednim rozdziale komenda mvn install spowodowała przeniesienie wygenerowanego na podstawie naszego projektu pliku JAR do lokalnego repozytorium:

Projekt zainstalowany w repozytorium m2

Plik POM naszego prostego projektu także został przeniesiony – jego rozszerzenie zostało zmienione z .xml na .pom. Zauważmy, że plik JAR został umieszczony w hierarchii katalogów, które są zgodne z polami groupId, artifactId, oraz version, z pliku pom.xml naszego projektu:

<groupId>com.kursjava.maven</groupId>
<artifactId>hello-maven</artifactId>
<version>1.0-SNAPSHOT</version>
Opis katalogu projektu w repozytorium m2

Od tej pory możemy korzystać z zależności do projektu hello-maven w innych projektach – wystarczy dodać taką zależność do pliku pom.xml. Przykład tego zagadnienia zobaczymy w jednym z kolejnych rozdziałów.

Możesz odpytać Maven o lokalizację lokalnego repozytorium .m2 wykorzystując poniższą komendę:
> mvn help:evaluate -Dexpression=settings.localRepository C:\Users\Przemek\.m2\repository

Uruchomienie aplikacji za pomocą pluginu Exec Maven

Aby uruchomić naszą prostą aplikację, możemy z linii poleceń wskazać maszynie wirtualnej Java, którą klasę z wygenerowanego pliku JAR uruchomić:

java -cp target/hello-maven-1.0-SNAPSHOT.jar com.kursjava.maven.HelloMaven Hello Maven!

Jeżeli jednak nasza klasa zawierałaby zależności do pewnych bibliotek, to musielibyśmy ręcznie ustawić classpath, aby znalazły się na nim wszystkie zależności naszego projektu. Prostszym sposobem uruchamiania aplikacji jest skorzystanie z pluginu Exec Maven – wystarczy wykonać następującą komendę, aby uruchomić klasę HelloMaven:

> mvn exec:java -Dexec.mainClass=com.kursjava.maven.HelloMaven [INFO] Scanning for projects... [INFO] [INFO] ----------------< com.kursjava.maven:hello-maven >---------------- [INFO] Building hello-maven 1.0-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- exec-maven-plugin:1.6.0:java (default-cli) @ hello-maven --- Hello Maven! [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.136 s [INFO] Finished at: 2020-03-05T20:00:54+01:00 [INFO] ------------------------------------------------------------------------

Pośród standardowych informacji wypisywanych przez Maven, widzimy na ekranie wynik wykonania klasy com.kursjava.maven.HelloMaven: Hello Maven!

Plugin exec:java nie rekompiluje klas naszego projektu – jeżeli zmieniłeś coś w swoim projekcie od ostatniego użycia exec:java, to zrekompiluj swój projekt za pomocą mvn compile.

Dodawanie zależności do projektu i pierwszy test jednostkowy

Na koniec pracy z naszym pierwszym, najprostszym projektem, dodamy do niego zależność do JUnit i umieścimy w nim jedną klasę z przykładowym testem.

Zależności zawieramy w elementach <dependency>, których elementem nadrzędnym jest element <dependencies> w pom.xml. W elemencie <dependency> podajemy odpowiednie groupId, artifactId, oraz version, których znaczenie już znamy. Opcjonalnie możemy także ustawić wartość dla parametru scope, który dokładniej mówię w rozdziale o zależnościach.

Gdy Maven będzie budował nasz projekt i zauważy w pliku pom.xml element <dependency>, to pobierze dla nas automatycznie wymaganą zależność i umieści ją w lokalnym repozytorium .m2.

Elementem scope możemy poinformować Maven, że dana zależność jest np. tylko wymagana podczas wykonywania testów, a w produkcyjnej wersji naszej aplikacji nie jest w ogóle używana. Tak też jest w tym przypadku – nasz projekt jest zależny od JUnit tylko podczas wykonywania testów – dlatego dodając zależność do JUnit ustawiamy element scope na test.

Aby nasz projekt mógł korzystać z JUnit, dodajemy do pliku pom.xml następujący element:

najprostszy-projekt/pom.xml
<!-- poczatek pliku pom.xml zostal pominiety -->

  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

W przypadku JUnit, zarówno groupId, jak i artifactId, mają taką samą wartość – junit.

Dodamy teraz przykładowy test do projektu. Testy w projektach Mavenowych umieszczamy w katalogu ${basedir}/src/test/java, więc musimy utworzyć katalogi test/java. Klasa z testem będzie w tym samym pakiecie, w którym znajduje się klasa główna projektu HelloMaven, więc utworzymy kolejne katalogi: com/kursjava/maven. Na koniec dodajemy plik z klasą HelloMavenTest.java o treści:

najprostszy-projekt/src/test/java/com/kursjava/maven/HelloMavenTest.java
package com.kursjava.maven;

import static org.junit.Assert.assertTrue;
import org.junit.Test;

public class HelloMavenTest {
  @Test
  public void shouldAnswerWithTrue() {
    assertTrue(true);
  }
}

Struktura naszego projektu powinna teraz wyglądać następująco:

najprostszy-projekt
|
|-- pom.xml
|
`-- src
    |
    `-- main
    |   |
    |   `-- java
    |       |
    |       `-- com
    |           |
    |           `-- kursjava
    |               |
    |               `-- maven
    |                   |
    |                   `-- HelloMaven.java
    `-- test
        |
        `-- java
            |
            `-- com
                |
                `-- kursjava
                    |
                    `-- maven
                        |
                        `-- HelloMavenTest.java

Testy w Maven uruchamiamy za pomocą komend mvn test:

> mvn test ... poczatek logów pominiety ... [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-maven --- [INFO] Changes detected - recompiling the module! [INFO] Compiling 1 source file to D:\kurs_maven\przyklady\najprostszy-projekt\target\test-classes [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-maven --- [INFO] Surefire report directory: D:\kurs_maven\przyklady\najprostszy-projekt\target\surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.kursjava.maven.HelloMavenTest Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.039 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.893 s [INFO] Finished at: 2020-03-06T18:47:54+01:00 [INFO] ------------------------------------------------------------------------

Maven wykrył w naszym projekcie nową klasę – najpierw ją skompilował, a następnie wykonał test korzystając w tym celu z pluginu maven-surefire-plugin. Na końcu Maven przedstawił podsumowanie wykonanych testów – jak widać powyżej, wykonany został jeden test, który zakończył się sukcesem.

Standardowo Maven pobiera zależności z oficjalnego repozytorium artefaktów. Wiele firm ma jednak własne repozytoria. Znajdują się w nich zbudowane pliki JAR z projektów tworzonych przez daną firmę i tylko dla niej dostępne. Aby Maven mógł odpytywać takie prywatne repozytorium, należy dodać do katalogu .m2 plik o nazwie settings.xml. Możemy w nim m. in. podać adres prywatnego repozytorium. Więcej informacji znajdziesz w oficjalnej dokumentacji Maven.

Pomijanie testów

Wykonywanie testów jednostkowych jest częścią procesu budowania projektów w Maven. Są one zawsze wykonywane przed wygenerowaniem pliku JAR (bądź WAR).

Czasem może się jednak zdarzyć, że mamy jakieś chwilowo niedziałające testy, a chcemy pomimo tego zbudować projekt – w takim przypadku możemy ustawić parametr maven.test.skip, aby nakazać Mavenowi jednorazowe pominięcie wykonania testów:

> mvn install -Dmaven.test.skip=true ... poczatek logów pominiety ... [INFO] Not copying test resources [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-maven --- [INFO] Not compiling test sources [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-maven --- [INFO] Tests are skipped. [INFO] [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-maven --- [INFO] Building jar: D:\kurs_maven\przyklady\najprostszy-projekt\target\hello-maven-1.0-SNAPSHOT.jar [INFO] [INFO] --- maven-install-plugin:2.4:install (default-install) @ hello-maven [INFO] Installing D:\kurs_maven\przyklady\najprostszy-projekt\target\hello-maven-1.0-SNAPSHOT.jar to C:\Users\Przemek\.m2\repository\com\kursjava\maven\hello-maven\1.0-SNAPSHOT\hello-maven-1.0-SNAPSHOT.jar [INFO] Installing D:\kurs_maven\przyklady\najprostszy-projekt\pom.xml to C:\Users\Przemek\.m2\repository\com\kursjava\maven\hello-maven\1.0-SNAPSHOT\hello-maven-1.0-SNAPSHOT.pom [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 1.699 s [INFO] Finished at: 2020-03-06T18:59:58+01:00 [INFO] ------------------------------------------------------------------------

Maven pominął kompilację (Not compiling test sources) oraz wykonanie testów (Tests are skipped) i przeszedł do wygenerowania pliku JAR i zainstalowania (przekopiowania) go w lokalnym repozytorium .m2.

Gdzie szukać zależności?

Możemy teraz zadać pytanie: skąd mamy wiedzieć, co wpisać w elemencie <dependency>, aby Maven pobrał odpowiednią zależność?

Aby znaleźć informację, jakich wartości powinniśmy użyć dla groupId, artifactId, oraz version, możemy zajrzeć na stronę:

https://repository.sonatype.org

Znajdziemy tam wyszukiwarkę, w której możemy wpisać szukaną przez nas zależność. Pasujące wyniki będą miały gotowy do skopiowania element <dependency>.

Innym sposobem jest po prostu wpisanie w Google nazwy biblioteki, z której chcemy skorzystać, z dodatkiem "maven", np. log4j maven.

Dodatkowo, dzięki popularności Mavena, często na oficjalnych stronach różnych projektów możemy znaleźć gotowy do przekopiowania element <dependency>. Dla przykładu, na stronie Lomboka (https://projectlombok.org/setup/maven) znajdziemy następującą informację:

<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.12</version>
  <scope>provided</scope>
</dependency>

Czasami informacja o projekcie zapisywana jest także jako: groupId:artifactId:version.

Zależność opisaną w takim formacie wystarczy otagować w elementy <dependency>, <groupId>, <artifactId>, oraz <version>, i dodać do pliku pom.xml.

Czyszczenie projektu – mvn clean

Pracując nad projektem dodajemy, usuwamy, oraz przemieszczamy w nim pliki. Zbudowanie projektu powoduje, że skompilowane i wygenerowane pliki umieszczane są w katalogu target, co widzieliśmy w jednym z poprzednich rozdziałów.

Jeżeli usuniemy bądź przemieścimy pliki, powinniśmy skorzystać z komendy mvn clean, która usuwa katalog target z całą jego zawartością. Dzięki temu możemy wykonać "czysty" build projektu – już bez usuniętych plików oraz z uwzględnieniem nowej lokalizacji tych przemieszczonych. Ma to znaczenie, ponieważ pliki te, pozostając w katalogu target po poprzednim buildzie, mogłyby w niechciany sposób wpłynąć na nowozbudowany projekt.

Przykład użycia mvn clean na naszym projekcie:

> mvn clean [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-maven --- [INFO] Deleting D:\kurs_maven\przyklady\najprostszy-projekt\target [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 0.294 s [INFO] Finished at: 2020-03-08T17:57:03+01:00 [INFO] ------------------------------------------------------------------------
Wykonanie komend Mavena możemy łączyć, pisząc je jedna po drugiej – często stosowana jest np. kombinacja mvn clean install – czyszczenie projektu i ponowne jego zbudowanie.

Generator archetypów

Aby stworzyć nowy projekt Maven nie musimy tworzyć struktury katalogów ręcznie. Zrobiliśmy to w poprzednim rozdziale, aby zaznajomić się z Mavenem.

Maven udostępnia plugin nazywany generatorem archetypów, który tworzy strukturę i szkielet projektu wybranego przez nas typu. Na moment pisania tego dokumentu dostępnych jest 2590 archetypów. My skorzystamy z maven-archetype-quickstart, który tworzy najprostszy projekt wykorzystujący Maven.

Gdy uruchamiamy komendę mvn archetype:generate, możemy podać wszystkie wymagane parametry, by projekt został wygenerowany od razu, lub nie podać ich, przez co Maven przejdzie w tryb interaktywnego tworzenia projektu.

W interaktywnym trybie Maven zapyta nas o kilka informacji, takich jak: groupId, artifactId, version, oraz numer archetypu, który chcemy wygenerować. Możemy zawęzić listę wyświetlanych archetypów wpisując część nazwy poszukiwanego rodzaju projektu (np. spring, aby wyświetlić archetypy związane ze Springiem).

Poniżej znajduje się przykład użycia generatora archetypów w trybie interaktywnym:

> mvn archetype:generate [INFO] >>> maven-archetype-plugin:3.1.2:generate (default-cli) > generate-sources @ standalone-pom >>> [INFO] [INFO] <<< maven-archetype-plugin:3.1.2:generate (default-cli) < generate-sources @ standalone-pom <<< [INFO] [INFO] --- maven-archetype-plugin:3.1.2:generate (default-cli) @ standalone-pom --- [INFO] Generating project in Interactive mode [INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0) Choose archetype: 1: remote -> am.ik.archetype:elm-spring-boot-blank-archetype (Blank multi project for Spring Boot + Elm) 2: remote -> am.ik.archetype:graalvm-blank-archetype (Blank project for GraalVM) (... lista archetypów pominięta ...) 2589: remote -> xyz.luan.generator:xyz-generator (-) 2590: remote -> za.co.absa.hyperdrive:component-archetype (-) Choose a number or apply filter (format: [groupId:]artifactId, case sensitive co ntains): 1497: 1497 Choose org.apache.maven.archetypes:maven-archetype-quickstart version: 1: 1.0-alpha-1 2: 1.0-alpha-2 3: 1.0-alpha-3 4: 1.0-alpha-4 5: 1.0 6: 1.1 7: 1.3 8: 1.4 Choose a number: 8: 8 Define value for property 'groupId': com.kursjava.maven Define value for property 'artifactId': wygenerowany-projekt Define value for property 'version' 1.0-SNAPSHOT: : Define value for property 'package' com.kursjava.maven: : Confirm properties configuration: groupId: com.kursjava.maven artifactId: wygenerowany-projekt version: 1.0-SNAPSHOT package: com.kursjava.maven Y: : [INFO] ------------------------------------------------------------------------ [INFO] Using following parameters for creating project from Archetype: maven-arc hetype-quickstart:1.4 [INFO] ------------------------------------------------------------------------ [INFO] Parameter: groupId, Value: com.kursjava.maven [INFO] Parameter: artifactId, Value: wygenerowany-projekt [INFO] Parameter: version, Value: 1.0-SNAPSHOT [INFO] Parameter: package, Value: com.kursjava.maven [INFO] Parameter: packageInPathFormat, Value: com/kursjava/maven [INFO] Parameter: package, Value: com.kursjava.maven [INFO] Parameter: groupId, Value: com.kursjava.maven [INFO] Parameter: artifactId, Value: wygenerowany-projekt [INFO] Parameter: version, Value: 1.0-SNAPSHOT [INFO] Project created from Archetype in dir: D:\kurs_maven\przyklady\wygenerowany-projekt [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 02:28 min [INFO] Finished at: 2020-03-09T19:14:00+01:00 [INFO] ------------------------------------------------------------------------

Ponieważ nie podałem parametru z numerem wskazującym na konkretny archetyp, Maven domyślnie proponował użycie archetypu maven-archetype-quickstart o numerze 1497. Zaznaczone powyżej na białym tle informacje to wartości podane przeze mnie – Maven potrzebował ich, aby wygenerować projekt. W niektórym miejscach zamiast wpisać wymaganą wartość nacisnąłem Enter, co spowodowało, że Maven skorzystał z proponowanej przez siebie domyślnej wartości.

W wyniku tej komendy wygenerowany został projekt o następującej strukturze:

wygenerowany-projekt
|
|-- pom.xml
|
`-- src
    |
    `-- main
    |   |
    |   `-- java
    |       |
    |       `-- com
    |           |
    |           `-- kursjava
    |               |
    |               `-- maven
    |                   |
    |                   `-- App.java
    `-- test
        |
        `-- java
            |
            `-- com
                |
                `-- kursjava
                    |
                    `-- maven
                        |
                        `-- AppTest.java

Projekt ma identyczną strukturę jak projekt utworzony przez nas w poprzednim rozdziale. Maven utworzył w nim klasę App, która wypisuje na ekran tekst "Hello World!" oraz klasę AppTest z jednym testem. W pliku pom.xml zawarte są informacje o naszym projekcie w polach groupId, artifactId, oraz version, a także zależność do JUnit. Projekt może od razu zostać zbudowany za pomocą mvn install.

Korzystanie z generatora archetypów to szybki sposób na stworzenie szkieletu projektu Mavenowego.

Użytą wcześniej komendę mvn archetype:generate moglibyśmy także użyć w trybie nieinteraktywnym gdybyśmy podali wymagane wartości jako parametry:

mvn archetype:generate -B -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=com.kursjava.maven -DartifactId=wygenerowany-projekt -Dversion=1.0-SNAPSHOT -Dpackage=com.kursjava.maven
  • -B (batch mode) – to parametr, dzięki któremu komenda ma wykonać się w trybie nieinteraktywnym,
  • archetypeGroupId i archetypeArtifactId – opisują, który archetyp chcemy użyć – moglibyśmy jeszcze podać wersję za pomocą archetypeVersion, ale pomijając ten parametr użyta zostanie najnowsza wersja archetypu,
  • groupId, artifactId, version – to znane nam już atrybuty opisujące nasz projekt,
  • package – pakiet, w którym mają znaleźć się nasze klasy.

Podsumowanie

W tym rozdziale poznaliśmy podstawy pracy z Mavenem:

  • Maven stosuje zasadę konwencja przed konfiguracją (convention over configuration) – aby Maven działał, wymagana jest minimalna konfiguracja – większość parametrów projektu ma domyślne wartości.
  • Projekty korzystające z Maven powinny mieć odpowiednią strukturę:
    • ${basedir}/src/main/java – tutaj umieszczamy kod Java,
    • ${basedir}/src/test/java – tutaj umieszczamy testy jednostkowe.
  • Jeżeli klasa ma być w pakiecie com.kursjava.maven, to powinniśmy umieścić ją w katalogu ${basedir}/src/main/java/com/kursjava/maven
  • ${basedir} to zmienna w Mavenie która oznacza główny katalog projektu, w którym zawarte są wszystkie pozostałe pliki i katalogi projektu.
  • Konfigurację Maven umieszczamy w pliku pom.xml, który powinien znajdować się w katalogu głównym projektu. Przykładowy, prosty plik pom.xml:
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <groupId>com.kursjava.maven</groupId>
  <artifactId>hello-maven</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
</project>
  • Powyższy plik pom.xml składa się z następujących elementów:
    • modelVersion – wersja pliku POM – powinna być ustawiana na wartość 4.0.0,
    • groupId, artifactId oraz version to elementy identyfikujące nasz projekt:
      • groupId – unikalny identyfikator grupy, do której należy ten projekt – zazwyczaj jest to odwrócona domena autora bądź firmy odpowiedzialnej za projekt, z ewentualnym dodatkiem identyfikującym podgrupę projektów,
      • artifactId – nazwa tego konkretnego projektu,
      • version – aktualna wersja projektu – wykonując zmiany w naszym projekcie i dokonując release'ów powinniśmy tą wersję aktualizować.
    • properties – parametry wpływające na kompilację kodu:
      • maven.compiler.source – źródła mają być traktowane jako kod Java w wersji 1.8,
      • maven.compiler.target – nasze klasy mają być kompilowane do wersji 1.8,
      • project.build.sourceEncoding – kodowanie czytanych i zapisywanych plików to UTF-8.
  • Aby zbudować projekt w Maven, korzystamy z komendy mvn install.
  • Proces budowy projektu składa się z faz (build lifecycle phases), które są wykonywane jedna po drugiej w celu zbudowanie projektu.
  • Fazy Mavena mają przypisane pluginy, który wykonują prace związane z daną fazą. Dla przykładu, kompilacją zajmuje się plugin maven-compiler-plugin, uruchamianiem testów maven-surefire-plugin, a generacją pliku JAR maven-jar-plugin.
  • Uruchomienie komendy mvn install powoduje wykonanie wielu operacji, ponieważ fazy budowania projektu są od siebie zależne – faza install wymaga pliku JAR generowanego w fazie package. Faza package wymaga przetestowania klas w fazie test, a faza test – skompilowanego w fazie compile kodu.
  • Wszystkie pliki wygenerowane podczas budowania projektu, w tym plik JAR, umieszczane są w katalogu o nazwie target w katalogu głównym projektu.
  • Maven generuje w katalogu target plik JAR o nazwie złożonej z połączonych wartości artifactId oraz version, które konfigurujemy w pliku pom.xml. Dla następujących wartości:
    <groupId>com.kursjava.maven</groupId>
    <artifactId>hello-maven</artifactId>
    <version>1.0-SNAPSHOT</version>
    Maven wygeneruje plik target/hello-maven-1.0-SNAPSHOT.jar
  • Maven przechowuje na dysku lokalne repozytorium artefaktów w katalogu .m2/repository użytkownika (np. C:\Users\Przemek\.m2). Znajdują się w nim:
    • pluginy, z których korzysta Maven,
    • zależności, z których korzystają nasze projekty,
    • plik JAR z naszymi projektami zbudowanymi za pomocą Mavena – dla przykładu, JAR z opisanego powyżej projektu hello-maven znajdzie się w lokalizacji opisanej na poniższym rysunku:
    Opis katalogu projektu w repozytorium m2
  • Maven automatycznie pobiera i umieszcza w lokalnym repozytorium .m2 pliki JAR bibliotek, których wymagają nasze projekty (np. JUnit, Spring, Hibernate itp.).
  • Jeżeli na projekcie używane jest prywatne repozytorium artefaktów, z którego Maven powinien pobierać zależności, to jego adres możemy ustawić w pliku settings.xml w katalogu .m2.
  • Aby sprawdzić lokalizację lokalnego repozytorium .m2 możesz użyć komendy:
    mvn help:evaluate -Dexpression=settings.localRepository
  • Aby uruchomić aplikację, możemy skorzystać z pluginu Exec Maven, który ustawi odpowiednio classpath (pamiętaj o rekompilacji za pomocą mvn compile, jeżeli wykonałeś zmiany od ostatniego uruchomienia projektu):
    mvn exec:java -Dexec.mainClass=com.kursjava.maven.HelloMaven
  • Zależności w projektach Mavenowych dodajemy do elementu <dependencies> w pliku pom.xml podając odpowiednią kombinacją wartości groupId, artifactId, oraz version:
<!-- poczatek pliku pom.xml zostal pominiety -->

  <properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>
  • Aby dowiedzieć się, co wpisać w elemencie <dependency>, możemy:
    • poszukać odpowiedzi na Google,
    • użyć wyszukiwarki na stronie https://repository.sonatype.org,
    • znaleźć na oficjalnej stronie danej biblioteki zależność zapisaną w formacie groupId:artifactId:version lub przekopiować element <dependency>, jeżeli jest dostępny.
  • Wykonanie testów przesz Maven osiągamy za pomocą komendy mvn test
  • Aby pominąć wykonanie testów należy ustawić parametr maven.test.skip:
    mvn install -Dmaven.test.skip=true
  • Aby wyczyścić pliki wygenerowane przez Maven w ramach budowania projektu korzystamy z komendy mvn clean
  • Aby wywołać więcej niż jedną komendę w Maven możemy zapisać je jedna po drugiej: mvn clean install
  • Generator archetypów to plugin w Maven pozwalający na wygenerowanie szkieletu projekt. Do wyboru jest wiele różnych archetypów. Archetypy można generować w trybie interaktywnym lub podać wszystkie wartości od razu.
  • Przykład wygenerowanie prostego archetypu w trybie nieinteraktywnym:
mvn archetype:generate -B -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DgroupId=com.kursjava.maven -DartifactId=wygenerowany-projekt -Dversion=1.0-SNAPSHOT -Dpackage=com.kursjava.maven

Zadania

Wygeneruj za pomocą generatora archetypów prosty projekt Mavenowy, a nastepnie:

  • w głównej klasie dodaj metodę kwadrat, która będzie zwracała kwadrat liczby podanej jako argument; nie usuwaj metody main,
  • w klasie z testami jednostkowymi dodaj kilka testów metody kwadrat,
  • zbuduj projekt za pomocą mvn install i sprawdź, czy wszystkie testy wykonały się bez błędów,
  • sprawdź, czy plik JAR wygenerowany przez Maven został przeniesiony do Twojego lokalnego repozytorium .m2,
  • usuń pliki wygenerowane podczas budowania projektu za pomocą komendy mvn clean,
  • ponownie zbuduj projekt, tym razem pomijając fazę testów,
  • uruchom swój program:
    • przy pomocy pluginu Exec Maven,
    • w klasyczny sposób korzystając z java w linii komend.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Nie musisz podawać swojego imienia, e-mailu, ani strony www, aby opublikować komentarz. Komentarze muszą zostać zatwierdzone, aby były widoczne na stronie.