Site icon Java blog

Narzędzia programisty #3: Maven postawy

apache maven

Jedną z ważniejszych zasad w programowaniu jest korzystanie z fragmentów kodu opracowanych już przez kogoś innego. Niektórzy nazywają tą regułę 'nie wymyślaniem koła od nowa’. Chodź z pozoru wydaje się, że dobry programista powinien wiedzieć wszystko i napisać kod sam od początku do końca, jest to błędne myślenie. We współczesnym programowaniu, bardzo ważna jest znajomość zewnętrznych bibliotek (frameworków) i umiejętne wykorzystanie ich potencjału w swojej aplikacji. Niestety obsługa dziesiątek zewnętrznych zależności nie jest łatwe. Z pomocą przychodzi tutaj narzędzie, służące do zarządzania projektami Java, o nazwie Maven.

Technologie służące do zarządzania projektami

Najprostszym sposobem budowania projektu w języku Java, jest skorzystanie z kompilatora Javac i wywołanie odpowiedniego polecenia za pomocą linii komend. Nie jest to jednak wydajne rozwiązanie, zwłaszcza w przypadku wielkich modułowych aplikacji. Stąd też, podczas rozwoju języka, szybko przyszedł czas na zautomatyzowanie fazy budowania projektu.

Pierwszymi, znanymi mi, sposobami na usprawnienie budowania programów opartych na JDK, było tworzenie tzw. makefile’i, czyli prostych skryptów, które sekwencyjnie wykonują operacje potrzebne do skompilowania kodu. Szczerze mówiąc, tylko raz spotkałem się z takim sposobem zarządzania projektem i nie sądzę, abyś kiedykolwiek musiał z niego korzystać. Powodem zażegnania używania makefile’i było skomplikowana składnia i trudność w tworzenia większych skryptów, opartych na specjalnych sekcjach.

Kolejnym etapem w rozwoju zarządzania projektami Java, była technologia o nazwie Ant. Pisanie skryptów, zastąpiono tu uszeregowaną strukturą xml. Xml to plik, który podobnie jak HTML, opiera się na korzystaniu ze specjalnych tagów, które porządkują czytelność skryptu, a także pozwalają je zagnieżdżać, tworząc strukturę drzewiastą. Ant bardzo ułatwił kompilację kodu, jednak wciąż był za skomplikowany. Ponad to nie posiada wbudowanego wsparcia zarządzania zależnościami, przez co nie ma zastosowania w nowoczesnym programowaniu (Ant nie potrafi w łatwy sposób pobierać zewnętrzne biblioteki, uzupełniające JDK).

Wszystkie powyższe problemy rozwiązywał Maven. Framework ten posiada prostą budowę opartą o xml. Umożliwia w łatwy sposób zarządzanie zależnościami i pluginami, czy obsługiwanie faz budowania projektu.

Instalacja narzędzia

Maven można używać z poziomu linii komend albo z poziomu IDE. Tak naprawdę oba sposoby nie wykluczają się wzajemnie, co więcej można powiedzieć, że się uzupełniają. Różnica jest taka, że w przypadku korzystania z IDE nie musisz nic ustawiać, ponieważ mają one przeważnie skonfigurowaną „wtyczkę” do Mavena. Dlatego też w tym wpisie skupię się na pracy z linii komend, która jest bardziej wymagająca.

Praca z linii komend na Linuksie

Instalacja Mavena, korzystając z powłoki systemu Linuks, jest bardzo łatwa. Wystarczy odpalić w terminalu polecenie:

sudo apt install maven

Jeśli nie wiesz, czy Maven nie został już zainstalowany, wystarczy, że wpiszesz po prostu mvn –version. A w terminalu powinna wyświetlić się prośba o zainstalowanie narzędzia. Maven wymaga instalacji JDK, więc jeśli jeszcze go nie posiadasz na swoim systemie, to bezwzględnie musisz to zrobić.

Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 11.0.15, vendor: Private Build, runtime: /usr/lib/jvm/java-11-openjdk-amd64
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "5.10.16.3-microsoft-standard-wsl2", arch: "amd64", family: "unix"

Maven na Windowsie

Tutaj sytuacja jest bardziej skomplikowana. W przypadku tej opcji należy ściągnąć fizyczną paczkę ze strony https://maven.apache.org/download.cgi, rozpakować w dowolnym katalogu a następnie ustawić zmienne środowiskowe w systemie (konkretnie zmienną MAVEN_HOME oraz PATH). W ten sposób konsola Windows zacznie 'dostrzegać’ narzędzie. W nowo stworzonej MAVEN_HOME powinien widnieć ścieżka, gdzie ściągnięty plik został rozpakowany (np. C:\Users\Mariusz\Development\maven\apache-maven-3.5.3). Natomiast do zmiennej PATH (powinna już istnieć) należy dodać wartość %MAVEN_HOME%\bin.

Tworzenie projektu za pomocą archetypu

Aby stworzyć nowy projekt wystarczy odpalić komendę mvn archetype:generate. Pozwoli ona stworzyć plik konfiguracyjny pom.xml, który zawiera wszelkie informacje dla frameworka, w jaki sposób budować projekt. Struktura samego pliku jest definiowana przez tzw. archetyp, który domyślnie ma poustawiane pewne funkcjonalności (np. pluginy, czy zależności). Najprostszym archetypem jest maven-archetype-quickstart, który tworzy najprostszy z możliwych szkieletów projektu. Żeby zacząć pracę, używając tego schematu, należy jedynie zdefiniować grupę i artefakt.

mvn archetype:generate -DgroupId=pl.developeronthego -DartifactId=maven-test -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

Jeśli wszystko zostało zdefiniowane prawidłowo, to w katalogu, gdzie została wywołana ta komenda, powinien powstać nowy katalog z nazwą artefaktu (w moim przypadku: maven-test), a w nim wyżej wspomniany plik pom.xml.

Zawartość pliku pom.xml wygląda tak:

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>pl.developeronthego</groupId>
  <artifactId>maven-test</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>maven-test</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Aby się upewnić, że projekt będzie zbudowany z właściwą wersją JDK, warto też dodać sekcję properties:

<properties>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
</properties>

Cykl życia projektu

Po stworzeniu projektu, framework pozwala wywoływać konkretną fazę jego budowania. Jest to bardzo wygodne rozwiązanie, ponieważ czasami nie chcesz przebudowywać całej aplikacji. Wystarczy Ci np. odpalenie samych testów jednostkowych (w celu sprawdzenia ich poprawności). Do tego służą właśnie fazy budowania.

validate – waliduje projekt, czy jest poprawny oraz czy wszystkie niezbędne informacje są dostępne

compile – wykonuje kompilację kodu w projekcie

test – odpala testy jednostkowe (np. za pomocą junit)

package – produkuje plik *jar po skompilowaniu kodu

verify – odpala testy integracyjne (np. służąc do sprawdzenia połączenia z bazą danych)

install – podstawowa komenda, buduje cały projekt i zapisuje rezultat w lokalnym repozytorium (patrz niżej)

deploy – w zależności od konfiguracji, umożliwia załadowanie gotowego projektu na środowisko deweloperskie

Przykład użycia fazy install:

mvn install

Każda z faz wywołuje poprzednią. Co oznacza, że np. package wcześniej odpala validate, następnie compile i na końcu test. Dodatkowo można użyć też opcji clean, która usunie stare niepotrzebne pliki wygenerowane podczas poprzedniego budowania projektu (ale nie usunie Twojego kodu).

mvn clean install

Sekcje pliku pom.xml

Plik konfiguracyjny pom.xml posiada z góry ustaloną strukturę drzewiastą, podzieloną na odpowiednie tagi. Oto najważniejsze z nich:

project – główna sekcja, zawierająca wszystkie inne. Jest to sekcja minimum, aby zbudować projekt. Obowiązkowo powinna zawierać tagi: modelVersion (wersja deskryptora Mavena), groupId (grupa, do której powinna należeć Twoja aplikacja), artifactId (nazwa artefaktu, czyli Twojego programu) i version (w jakiej wersji jest Twój projekt).

dependencies – miejsce, w który wypisujesz wszystkie biblioteki potrzebne do pracy nad Twoim projektem. Biblioteki zostaną pobrane ze zdalnego repozytorium automatycznie i dołączone do Twojego kodu, tak, że będziesz móc je używać jak zwykłe importy. Definiując zależności, podobnie jak w części głównej, wskazujesz grupę (groupId), artefakt (artifactId) oraz numer wersji biblioteki (version), którą chcesz pobrać. Możesz też opcjonalnie przypisać zakres (scope), w którym momencie cyklu życia zależność powinna zostać pobrana. Domyślnie zależności będą pobierane w fazie compile (czyli prawie najniższej), ale np. biblioteki do testowania mogą być pobieranie np. dopiero, gdy cykl osiągnie fazę test.

build – tag, w którym z reguły umieszcza się zestawienie wszelkich pluginów. Pozwala też pokazać miejsce przechowywania wszelkich zasobów (ang. resources).

plugins – ważna sekcja wewnątrz tagu build, zawiera konfigurację wtyczek, które będą uruchamiane podczas budowania aplikacji.

Struktura projektu

Nie tylko plik konfiguracyjny jest z góry ustalony. Projekty Maven posiadają też domyślne rozmieszczenie katalogów. Najczęściej używane z nich to:

src/main/java – miejsce przechowywania pakietów zawierających kod aplikacji

src/main/resources – zawiera zasoby potrzebne dla projektu (tu np. możesz trzymać skrypt tworzący bazę danych)

src/test/java – w tym katalogu powinny być zawarte testy jednostkowe

src/test/resources – zasoby dla testów

src/main/webapp – przydatne do aplikacji internetowych, np. do przechowywania plików HTML

Repozytoria

Teraz najważniejszą rzeczą dla Ciebie jest zrozumienie w jaki sposób zarządzane są zależności. Większość bibliotek, napisanych w Javie jest dostępnych na publicznych, zdalnych repozytoriach (czyli serwerach). Domyślnym z nich jest tzw. repozytorium centralne (central), dostępne pod adresem: https://repo1.maven.org/maven2/. To nie jedyny serwer tego typu. Całą ich listę znajdziesz na stronie: https://mvnrepository.com/repos. Dodatkowo wiele firm, ze względów bezpieczeństwa, tworzy własne repozytoria zdalne (zwane artifactory, od artifact). Tobie jednak na etapie nauki wystarczy wiedza o tym skąd są pobierane biblioteki oraz gdzie lokalnie są przechowywane.

Jeśli chcesz odnaleźć ściągnięte biblioteki, użyte w Twoim projekcie, to domyślnie znajdują się one w Twoim katalogu domowym w folderze ukrytym o nazwie .m2. W nim z kolei znajduje się Twoje lokalne repozytorium w katalogu o nazwie repository. Jeśli udało Ci się zbudować projekt za pomocą komendy mvn install, to w tym miejscu będą widnieć zarówno zależności do Twojej aplikacji jak i ona sama.

Zawartość mojego lokalnego repozytorium po zbudowaniu projektu

Przykładowo na obrazku powyżej widać, że została zaciągnięta biblioteka junit. Rozkład katalogów jest zbieżny z danymi w polach obowiązkowych w pliku pom.xml każdej z zależności, zgodnie z schematem groupId/artifactId/version. Ponieważ wartość groupId to 'junit’, po wyświetleniu folderu repository zobaczysz właśnie tą nazwę, a w niej znów katalog 'junit’, a następnie numer wersji (tutaj będzie to 3.8.1). W ten sposób Maven kataloguje sobie wszystkie biblioteki.

Nie inaczej dzieje się z moim projektem. Znajduję go pod ścieżką: pl/developeronthego/maven-test/1.0-SNAPSHOT/. W ten też sposób moja własna aplikacja, staje się też dostępna dla innych projektów, nad którymi pracuję lokalnie. Oczywiście, gdybym chciał podzielić się swoją biblioteką z innymi, musiałbym założyć swoje własne artifaktory i skonfigurować odpowiednio Mavena.

Konfiguracja

W katalogu .m2 znajdziesz nie tylko folder repository, ale także plik settings.xml, który kontroluje ustawienia Mavena. To właśnie tam możesz wskazać z jakiego serwera pobierane są zależności, czy ustawić proxy.

Sekcje w pliku settings.xml:

Servers – zawiera informacje wrażliwe (hasła, nazwy użytkowników) potrzebne do logowania się do zdalnego repozytorium, wskazanego w pom.xml (w tagu: <repositories>). Przydatne do pracy z wewnętrznym artifactory albo do pobierania dodatkowych bibliotek (np. w przypadku korzystania z frameworka Primefaces)

Mirrors – tutaj deklarujesz alternatywne do domyślnego repozytoria.

Proxies – ustawianie serwera proxy. Przydaje się, gdy komputer korzysta z zewnętrznego proxy. Często będziesz konfigurować tą opcję, gdy pracujesz z komputera pracowniczego.

Profiles – aby grupować powyższe ustawienia, warto ustawić odpowiednie profile, tak aby w łatwy sposób zamieniać jedne ustawienia na drugie. Przykładowo, pracujesz na dwóch projektach. Jeden korzysta z jednego repozytorium a drugi z innego.

ActiveProfiles – profile wypisane tutaj są automatycznie używane.

Jeśli chciałbyś manualnie wskazać, z którego profilu, to wystarczy użyć flagi -P.

mvn install -P github

Nazwa github powinna być wypisana w tagu <id> w sekcji <profile>.

Przykładowy plik settings.xml

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0
                      http://maven.apache.org/xsd/settings-1.0.0.xsd">

  <activeProfiles>
    <activeProfile>github</activeProfile>
  </activeProfiles>

  <profiles>
    <profile>
      <id>github</id>
      <repositories>
        <repository>
          <id>central</id>
          <url>https://repo1.maven.org/maven2</url>
        </repository>
        <repository>
          <id>github</id>
          <url>https://maven.pkg.github.com/OWNER/REPOSITORY</url>
          <snapshots>
            <enabled>true</enabled>
          </snapshots>
        </repository>
      </repositories>
    </profile>
  </profiles>

  <servers>
    <server>
      <id>github</id>
      <username>USERNAME</username>
      <password>TOKEN</password>
    </server>
  </servers>
</settings>

Podsumowanie

Maven to wciąż najpopularniejszy framework do zarządzania projektem. Jego prostota sprawia, że ciężko znaleźć do jego alternatywę, choć pod kątem samych możliwości przebija go np. Gradle. Warto jednak znać podstawy tego narzędzia, ponieważ szanse na spotkanie się z nim w projekcie komercyjnym są bardzo wysokie.

* Plik ten jest archiwum (podobnym do plików zip czy rar), zawierającym skompilowany kod projektu Java. Taki plik jest przenośny i umożliwia np. uruchamiane go za pomocą serwerów aplikacji.

Przykładowy projekt, omawiany w lekcji: https://github.com/developeronthego/maven-test

Exit mobile version