Multi-module project

By 2015.02.19Maven

Több modulból álló Maven projektek

Korábbi bejegyzéseinkben megismerkedhettél a legfontosabb Maven komponensekkel, s most szeretném bemutatni, hogy hogyan lehet több modulból álló Maven projektet (multi-module project) létrehozni, melynek segítségével többrétegű alkalmazások készíthetők.

De miért is jó, ha több modulba szervezzük projektjeinket? Alapvetően a Java projektek osztályait package-ekbe szokás szervezni, ezzel könnyebben átláthatóvá téve a projektet. Ám egy-egy nagyobb projekt több ezer osztályt, s több száz package-et is tartalmazhat, s ez indokolttá teheti egy újabb absztrakció bevezetését a projekt szerkezetébe.  Egy projekten belül lehetőségünk van rá, hogy az alkalmazás fő funkcionalitásai mentén modulokba szervezzük a package-eket és az osztályokat. Tipikusan a többrétegű alkalmazások fejlesztése során használható ilyen projektstruktúra. Például az adatbázisműveletek, a háttérfolyamatok és a kezelőfelület forrásállományai is külön modulokba célszerű helyezni. A többrétegű, vagy MVC (Model-View-Controller) architektúrájú alkalmazásokról a tizennégyhetes online alaptréningen (amire itt tudsz jelentkezni) olvashatsz bővebben. A Maven segítségével szerencsére pofonegyszerűen készíthetünk multi-modul projekteket.

Egy multi-module projekt felépítése

Egy MVC architektúrájú, multi-modulos projekt esetén az alábbi módon nézhet ki a projekt könyvtárszerkezete:

Amint láthatod, a projekt pom fájlon kívül, minden modul rendelkezik egy pom fájllal. A modulok felépítése alapvetően ugyanolyan, mint a korábban megismert modul nélküli projekteké. Tehát kvázi, mintha több kicsi helloworld-öt kapcsolnánk logikailag össze. Minden modulban található egy-egy src könyvtár, mely tartalmaz egy main (forrásállományok) és egy test (tesztek) mappát.

A projekt pom fájlban definiált dependenciák, property-k, pluginek, stb. minden modulban érvényesek lesznek. Továbbá itt kell megadni, hogy projektünk milyen modulokat tartalmaz (<modules> tag-ek közt). Ezt, a fenti ábrán bemutatott projekt esetében az alábbi kód segítségével tehetjük meg:

<modules>
  <module>model</module>
  <module>controller</module>
  <module>view</module>
</modules>

Továbbá a projekt packaging módját pom-ra kell állítani:

<packaging>pom</packaging>

Minden modul pomjában meg kell adni az őt tartalmazó projekt adatait, a tag-ek közt:

<parent>
  <artifactId>demo</artifactId>
  <groupId>hu.zerotohero</groupId>
  <version>${project.version}</version>
</parent>

Amennyiben egy modulban szeretnénk használni egy másik modul osztályait, akkor azt dependenciaként kell sorolni az adott modulban. Például ha a controller modulban akarjuk használni a model modul tartalmát akkor az alábbi dependenciát kell megadni a controller pomjában:

<dependency>
  <groupId>hu.zerotohero</groupId>
  <artifactId>model</artifactId>
  <version>${project.version}</version>
</dependency>

Öröklődés

Ahogy azt már korábban említettem az egyes modulok pom-jai öröklik a szülőik pomjainak tartalmát. Vagyis a korábbi példa esetében, a model, a view és a controller pomjai is örökölték a projekt pom-jának tartalmát. Ezen felül maga a projekt pom is örökölt az úgynevezett Super Pom-tól, mely a Maven default pom-ja. A Super Pom-ot minden pom fájl megörökli implicit, vagy explicit módon (jelen esetben a projekt pom-on keresztül öröklik meg a modulok pom-jai). A Maven több szintű öröklődést is tud kezelni, vagyis, ha a modulokon belül lennének még modulok, akkor azok pom-jai nem csak a szülő pom tartalmát, de a projekt pom-ot és a Super Pom-ot is örökölnék. Emiatt, hogy több szintről tudunk örököltetni benne dolgokat, okozhatunk magunknak néhány kellemetlen órát, ha ugyanabból a dependenciából két különböző verziót használunk, ugyanis valamelyik library problémázni fog, és ilyenkor általában elhangzik a következő mondat a fejlesztő szájából : ” hát ez hülye, itt van bíííp, pont itt van behúzva te bíííp”… :) tehát nem árt átgondolni, a struktúrát a dependenciákat illetően.

A Super Pom mellett az előző bejegyzésben bemutatott settings.xml tartalmát is örökli a projekt minden pom fájlja.

Az öröklődéssel összerakott leíróhalmazt nevezzük effective POM-nak. Az effective POM tartalmaz minden leírót, amit az adott POM használatakor a maven fel fog oldani az öröklődésben.

Hogyan nézzük meg az effective pom-ot?

  • Console:

mvn help:effective-pom

  • Eclipse:

    A pom fájlt megnyitod, és lesz egy olyan “füled”, hogy Effective Pom.

  • Idea:

    A pom fájlon jobb klikk -> Maven -> “Show Effective POM”

Problémás eset

Mit kell tennünk, ha egy dependencia hoz magával egy másik dependenciát (tranzitív dependencia), de arra  a “másikra” nincs szükségünk a projektünkben?

Előfordult a következő: vaadin-os compile widgetset elhasal, és mi csak annyit látunk a git historyban, hogy bekerült pár újabb dependencia, config nem változott, és értetlenül nézünk az eset elé. No de nem túl sokáig, hiszen van a mavennek egy -X kapcsolója, valamint lelkesen elkezdünk kutatni, hogy “mi a bíííp lehet ezzel a bííííppel?!”

Majd 1-2 óra elteltével megvan a ludas, jött egy jasperreports-os dependency, aminek van egy jdtcore “hozománya”, és ez cseszi szét a compile-t. Mi legyen a megoldás? A jasperre szükségünk van, okkal használjuk ennél a projektnél, ki mégsem dobhatjuk…

Exclusion a megoldás:

        <dependency>
            <groupId>net.sf.jasperreports</groupId>
            <artifactId>jasperreports</artifactId>
            <version>${jasperReports.version}</version>
            <exclusions>
                <exclusion>
                    <artifactId>jdtcore</artifactId>
                    <groupId>eclipse</groupId>
                </exclusion>
            </exclusions>
        </dependency>