Sziasztok!
Korábbi bejegyzésünkben megismerkedtünk a Mavennel, s most az egyik, ha nem a leghasznosabb szolgáltatását, a függőségek (dependenciák) kezelését szeretném bemutatni egy példaprogramon keresztül.
A Demo Projekt
Bemutatóként egy olyan projektet szeretnénk készíteni, mely kiírja egy paraméterként megadott szám faktoriálisát. Természetesen megírhatnánk saját magunk is azt a metódust, mely kiszámolja a faktoriálist, ám tudomásunkra jutott, hogy az Apache Commons Math programkönyvtár tartalmazza a faktoriális függvény implementációját, s úgy döntöttünk, hogy azt a megoldást fogjuk használni. Emiatt projektünknek tartalmaznia kell majd a Commons Math könyvtárt tömörítő jar állományt. Ennek tudatában készítsük el új Maven projektünket, melynek pom.xml a következőképp fog kinézni:
<?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>hu.zerotohero</groupId>
<artifactId>maven_dependency_demo</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.3</version>
</dependency>
</dependencies>
</project>
Amint láthatod, a függőségeket a <dependencies> tag-ek között kell felsorolni, míg a <dependency> tag-ek közt az egyes függőségek adatait (groupId, artifactId, version) kell megadni. Projektünk fordításakor a megadott információk alapján a Maven Central repository-ból letöltődnek gépünkre a pom fájlban felsorolt függőségek.
Most nézzük a kódunkat:
package hu.zerotohero;
import org.apache.commons.math3.exception.MathArithmeticException;
import org.apache.commons.math3.util.CombinatoricsUtils;
public class MavenDemo {
public static final Integer DEFAULT_FACTORIAL_BASE = 5;
public static void main(String[] args) {
Integer base = determinateBaseNumber(args);
printFactorialToStandardOutput(base);
}
private static Integer determinateBaseNumber(String[] args) {
try {
return Integer.parseInt(args[0]);
} catch (Exception e) {
return DEFAULT_FACTORIAL_BASE;
}
}
private static void printFactorialToStandardOutput(Integer base) {
System.out.println(base + "! = " + calculateFactorialAsString(base));
}
private static String calculateFactorialAsString(Integer base) {
String factorialString = "";
try {
Long factorial = CombinatoricsUtils.factorial(base);
factorialString = factorial.toString();
} catch (MathArithmeticException e) {
factorialString = "Sajnos az eredmény túl nagy lett :(";
}
return factorialString;
}
}
Amint láthatod, alkalmazásunk a konzolból megkapott első paramétert megpróbálja számmá konvertálni, s ez alapján kiszámolni annak faktoriálisát. Amennyiben a megadott paraméter érvénytelen volt, vagy egyáltalán nem volt paraméter, akkor az 5 faktoriálisát fogjuk kiírni a standard outputra. A calculateFactorialAsString metódusunkban számoljuk ki a faktoriálist, mégpedig úgy, hogy meghívjuk a Commons Math könyvtár CombinatoricsUtils osztályának factorial metódusát. Ezt a pom fájlban megadott, majd a Maven által letöltött dependenciának köszönhetjük.
Futtatás
A dependenciákat a target/dependency könyvtárba másoltatom, így könnyebb lesz futtatni, illetve nem árt, ha ilyeneket is tudunk (btw, erről még lesz szó).
$ mvn clean install dependency:copy-dependencies
$ java -cp "target/maven_dependency_demo-1.0.jar:target/dependency/*" hu.zerotohero.MavenDemo 10
windows-on (Egy darab pontosvessző a különbség, így ez a helyes parancs):
$ java -cp "target/maven_dependency_demo-1.0.jar;target/dependency/*" hu.zerotohero.MavenDemo 10
10! = 3628800
$
Dependencia Scope
Lehetőségünk van beállítani, hogy az egyes dependenciák, alkalmazásunk életciklusának mely szakaszában lépjenek életbe. Erre azért lehet szükség, mert korlátozni szeretnénk az egyes függőségek élettartalmát, vagy befolyásolni akarjuk a fordítási folyamataink menetét.
Összesen 6 ilyen scope létezik, melyek közül kezdetben a következőket érdemes megemlíteni:
- compile: Ez az alapvető scope. Amennyiben nem állítunk be scope-ot, ebben a módban használja a Maven a függőséget. Ebben az esetben a dependencia minden fordításnál betöltődik a projektünkbe, illetve letöltődik, ha nincs még fenn a gépünkön.
- runtime: Ezt a scope-ot akkor használjuk, ha a dependenciára nincs szükség fordítás során, csak futtatás közben.
- test: Ezt a scope-ot akkor használjuk, ha csak a tesztjeinknek van szüksége az adott függőségre.
A példánkban szereplő dependencia scope-ja compile, ezért ezt nem volt szükséges beleírnunk a pom fájlba, viszont az alábbi módon megtehetjük (igaz, ez nem fog befolyásolni semmit):
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.3</version>
<scope>compile</scope>
</dependency>
Verziók
Ahogy láthatod, minden függőségnél megadhatjuk, hogy mely verziót töltse le belőle a Maven. Áltralában érdemes konkrét verziószámot megadni (mint ahogy az a példánkban is szerepelt), de a Maven lehetőséget biztosít arra, hogy verziószámok tartományát adjuk meg.
Tartomány megadásánál zárójelek közé kell két verziószámot írni. Az első szám jelképezi az intervallum kezdetét a második a végét. Amennyiben a zárójel szögletes, a megadott szám részét képezi az intervallumnak, sima zárójel esetén viszont nincs benne ( Pl.: [2.1, 2.9] ). A tartományok egyik leghasznosabb felhasználási módja, hogy rávehetjük a Mavent arra, hogy mindig a dependencia legfrissebb verzióját használja. Példánkat ennek megfelelően ki is egészíthetjük:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>[3.3,)</version>
<scope>compile</scope>
</dependency>
Dependenciánk verziója [3.3,)-re változott. Most a második számot kihagytuk, s ez által nem adtunk meg felső határt, mivel jelenleg nem tudjuk, hogy mely verziót szeretnénk majd a jövőben letölteni. Dependenciák legfrissebb verziója a 3.3-as, így célszerű azt megadni legkisebb verziószámnak.
Repository-k
A repository nem más, mint az a tárhely ahonnan a Maven le tudja tölteni az ott megtalálható függőségeket. A Maven rendelkezik egy úgynevezett central avagy központi repository-val (http://mvnrepository.com/), melyet alapértelmezetten használ minden Maven projekt. Természetesen léteznek kisebb repository-k is, melyek például egy-egy gyártó termékeit tartalmazhatják, de akár mi is létrehozhatunk egy sajátot. Munkám során nem egy olyan projektben dolgoztam, ahol saját gyártású dependenciákat is használtunk, így a cég készített egy repository-t annak érdekében, hogy minden projekt számára elérhető legyen az összes függőség. A példa alkalmazásunkban található dependencia a Maven központi repository-ban található, így nem kellett megadnunk új repository-t pom fájlunkban. A Demo kedvéért azért írjuk bele a következőt:
<repositories>
<repository>
<id>central</id>
<url>http://repo1.maven.org/maven2</url>
</repository>
</repositories>
Amint láthatjuk az egyes repository-kat a tag-ek között kell felsorolni (ezt a részt az átláthatóság kedvéért célszerű a fájl elejére írni), a repository tulajdondonságait pedig a tag-ek közt. Mindenképpen meg kell adni a repository id-át, valamint az URL-t, amin elérhető.
Igaz, hogy demo projektünk számára szükségtelen, de szeretnék bemutatni egy másik repository-t a példa kedvéért:
<repository>
<id>spring-milestone</id>
<name>Spring Maven MILESTONE Repository</name>
<url>http://maven.springframework.org/milestone</url>
</repository>
Ebből a repository-ból a Spring framework mérföldkő (milestone) verzióit lehet letölteni, melyek nem biztos, hogy stabilak, de egyes projektekben szükség lehet rájuk.
Egy későbbi bejegyzésben írunk majd a Maven plugin-jairól (kiegészítőiről). Érdemes megjegyezni, hogy a plugin-eknek is vannak repository-ai, amit PluginRepository-nak hívnak, s az alábbi módon hivatkozhatunk rájuk a pom fájlban:
<pluginRepositories>
<pluginRepository>
<id>...</id>
<url>...</url>
</pluginRepository>
</pluginRepositories>
Amint láthatjuk a PluginRepository-kat gyakorlatilag úgy kell felsorolni, mint a sima repository-kat, csak a tag-ek neve tér el.
Végezetül fontos szót ejtenünk a gépünkön megtalálható lokális repository-ról. Ez gyakorlatilag az a könyvtár, ahova a Maven az összes projektünk által használt függőséget letölti, s a beállításokat is tartalmazó .m2 mappában található. A Maven fordításonként csak azokat a függőségeket tölti le, melyek nem találhatóak meg a lokális repository-ban (vagyis az új, illetve a megváltozott verziószámú dependenciákat). Ezt Te is le tudod ellenőrizni a példánk által használt commons-math3-3.3.jar megvizsgálásával. Az első fordítás során a jar fájl letöltődött, de a későbbi fordítások során csak akkor fog egy új fájl letöltődni, ha újabb Apache Commons Math verzió került fel a központi repository-ba. Lehetőséged van rá, hogy kézzel kitöröld a dependenciákat, s arra is, hogy egy, a gépeden található jar-t telepíts saját lokális repository-dba. Ez akkor jöhet jól, ha többen dolgoztok egy projekten, nincs saját repository-tok, viszont van egy általatok készített dependencia, melyet mindannyian használni szeretnétek. Például, ha egy /home/zero/hero.jar nevű dependenciát szeretnék a lokális repository-ba telepíteni, akkor azt az alábbi paranccsal tehetném meg:
mvn install:install-file -Dfile=/home/zero/hero.jar -DgroupId=hu.zerotohero -DartifactId=hero -Dversion=1.0 -Dpackaging=jar
Amint láthatod a maven dependenciák segítségével egyszerűen, s gyorsan lehet bővíteni projektjeink eszköztárát, a verziókezelés megkönnyíti a projekt karbantarthatóságát, a különféle repository-k pedig jól konfigurálhatóvá teszik a projekteket.