Maven-applicatie met meerdere modules met Java-modules

1. Overzicht

Het Java Platform Module System (JPMS) voegt meer betrouwbaarheid, betere scheiding van problemen en sterkere inkapseling toe aan Java-applicaties. Het is dus geen build-tool het mist de mogelijkheid om projectafhankelijkheden automatisch te beheren.

Natuurlijk kunnen we ons afvragen of we dat kunnen gebruik beproefde build-tools, zoals Maven of Gradle, in modulaire toepassingen.

Eigenlijk kunnen we! In deze tutorial we zullen leren hoe u een Maven-applicatie met meerdere modules kunt maken met behulp van Java-modules.

2. Maven-modules inkapselen in Java-modules

Sinds modulariteit en afhankelijkheidsbeheer zijn niet elkaar uitsluitende concepten in Java, we kunnen het JPMS bijvoorbeeld naadloos integreren met Maven en zo het beste van twee werelden benutten.

In een standaard Maven-project met meerdere modules voegen we een of meer onderliggende Maven-modules toe door ze onder de hoofdmap van het project te plaatsen en ze te declareren in de bovenliggende POM, binnen de sectie.

Op onze beurt bewerken we de POM van elke onderliggende module en specificeren we de afhankelijkheden ervan via de standaard <groupId>, <artefactId> en <versie> coördinaten.

De reactor mechanisme in Maven - verantwoordelijk voor het afhandelen van projecten met meerdere modules - zorgt ervoor dat het hele project in de juiste volgorde wordt gebouwd.

In dit geval gebruiken we in principe dezelfde ontwerpmethodologie, maar met een subtiele maar fundamentele variant: we verpakken elke Maven-module in een Java-module door er het module-descriptorbestand aan toe te voegen, module-info.java.

3. De bovenliggende Maven-module

Om te laten zien hoe modulariteit en afhankelijkheidsbeheer goed samenwerken, zullen we een eenvoudig Maven-demo-project met meerdere modules bouwen, waarvan de functionaliteit wordt beperkt tot het ophalen van enkele domeinobjecten uit een persistentielaag.

Om de code simpel te houden, gebruiken we een plain Kaart als de onderliggende datastructuur voor het opslaan van de domeinobjecten. Natuurlijk kunnen we verderop gemakkelijk overschakelen naar een volwaardige relationele database.

Laten we beginnen met het definiëren van de bovenliggende Maven-module. Om dit te bereiken, maken we een root-projectdirectory aan met de naam, bijvoorbeeld multimodulemavenproject (maar het kan iets anders zijn), en voeg er de ouder aan toe pom.xml het dossier:

com.baeldung.multimodulemavenproject multimodulemavenproject 1.0 pom multimodulemavenproject org.apache.maven.plugins maven-compiler-plugin 3.8.0 11 11 UTF-8 

Er zijn een paar details die het vermelden waard zijn in de definitie van de bovenliggende POM.

Ten eerste, aangezien we Java 11 gebruiken, we hebben minimaal Maven 3.5.0 nodig op ons systeem, aangezien Maven vanaf die versie Java 9 en hoger ondersteunt.

En we hebben ook ten minste versie 3.8.0 van de Maven-compilerplug-in nodig. Laten we er daarom voor zorgen dat we de nieuwste versie van de plug-in op Maven Central controleren.

4. De Child Maven-modules

Merk op dat tot nu toe de bovenliggende POM declareert geen onderliggende modules.

Omdat ons demoproject enkele domeinobjecten uit de persistentielaag haalt, zullen we vier kind-Maven-modules maken:

  1. entiteitsmodule: bevat een eenvoudige domeinklasse
  2. daomodule: bevat de interface die nodig is voor toegang tot de persistentielaag (een basis DAO-contract)
  3. userdaomodule: omvat een implementatie van de daomodule‘S interface
  4. mainappmodule: het startpunt van het project

4.1. De entiteitsmodule Maven-module

Laten we nu de eerste Maven-module voor het kind toevoegen, die alleen een basisdomeinklasse bevat.

Laten we onder de hoofdmap van het project het entiteitmodule / src / main / java / com / baeldung / entity directorystructuur en voeg een Gebruiker klasse:

openbare klasse Gebruiker {privé laatste String naam; // standaard constructor / getter / toString}

Laten we vervolgens de module's toevoegen pom.xml het dossier:

 com.baeldung.multimodulemavenproject multimodulemavenproject 1.0 com.baeldung.entitymodule entitymodule 1.0 jar entitymodule

Zoals we kunnen zien, is de Entiteit module heeft geen enkele afhankelijkheid van andere modules, noch heeft het extra Maven-artefacten nodig, aangezien het alleen de Gebruiker klasse.

Nu moeten we inkapselen de Maven-module in een Java-module. Om dit te bereiken, plaatsen we simpelweg het volgende module-descriptorbestand (module-info.java) onder de entiteitmodule / src / main / java directory:

module com.baeldung.entitymodule {exporteert com.baeldung.entitymodule; }

Laten we tot slot de onderliggende Maven-module toevoegen aan de bovenliggende POM:

 entiteitsmodule 

4.2. De daomodule Maven-module

Laten we een nieuwe Maven-module maken die een eenvoudige interface zal bevatten. Dit is handig voor het definiëren van een abstract contract voor het ophalen van generieke typen uit de persistentielaag.

Er is trouwens een zeer dwingende reden om deze interface in een aparte Java-module te plaatsen. Hierdoor hebben we een abstract, sterk ontkoppeld contract, dat gemakkelijk in verschillende contexten kan worden hergebruikt. In de kern is dit een alternatieve implementatie van het Dependency Inversion Principle, wat een flexibeler ontwerp oplevert.

Laten we daarom het daomodule / src / main / java / com / baeldung / dao directorystructuur onder de hoofdmap van het project, en voeg hieraan de Dao koppel:

openbare interface Dao {Optioneel findById (int id); Lijst findAll (); }

Laten we nu de module's definiëren pom.xml het dossier:

 // bovenliggende coördinaten com.baeldung.daomodule daomodule 1.0 jar daomodule

De nieuwe module vereist ook geen andere modules of artefacten, dus we verpakken het gewoon in een Java-module. Laten we de module-descriptor maken onder de daomodule / src / main / java directory:

module com.baeldung.daomodule {exporteert com.baeldung.daomodule; }

Laten we tot slot de module toevoegen aan de bovenliggende POM:

 entiteitmodule daomodule 

4.3. De userdaomodule Maven-module

Laten we vervolgens de Maven-module definiëren die een implementatie van het Dao koppel.

Laten we onder de hoofdmap van het project het userdaomodule / src / main / java / com / baeldung / userdao directory-structuur, en voeg hier het volgende aan toe Gebruiker klasse:

public class Userensional implementeert Dao {private final Map-gebruikers; // standard constructor @Override public Optioneel findById (int id) {return Optional.ofNullable (users.get (id)); } @Override openbare lijst findAll () {retourneer nieuwe ArrayList (users.values ​​()); }}

Simpel gezegd, de Gebruiker class biedt een basis-API waarmee we Gebruiker objecten uit de persistentielaag.

Om het simpel te houden, hebben we een Kaart als de ondersteunende datastructuur voor het behouden van de domeinobjecten. Het is natuurlijk mogelijk om een ​​meer grondige implementatie te bieden die bijvoorbeeld gebruikmaakt van de entiteitsbeheerder van Hibernate.

Laten we nu de POM van de Maven-module definiëren:

 // bovenliggende coördinaten com.baeldung.userdaomodule userdaomodule 1.0 jar userdaomodule com.baeldung.entitymodule entitymodule 1.0 com.baeldung.daomodule daomodule 1.0 

In dit geval zijn de dingen iets anders, zoals de userdaomodule module vereist de entitymodule en daomodule modules. Daarom hebben we ze als afhankelijkheden in het pom.xml het dossier.

We moeten deze Maven-module nog steeds inkapselen in een Java-module. Laten we dus de volgende modulebeschrijving toevoegen onder de userdaomodule / src / main / java directory:

module com.baeldung.userdaomodule {vereist com.baeldung.entitymodule; vereist com.baeldung.daomodule; voorziet com.baeldung.daomodule.Dao van com.baeldung.userdaomodule.Userbao; exporteert com.baeldung.userdaomodule; } 

Ten slotte moeten we deze nieuwe module toevoegen aan de bovenliggende POM:

 entiteitmodule daomodule userdaomodule 

Op hoog niveau is dat gemakkelijk te zien de pom.xml bestand en de module descriptor spelen verschillende rollen. Toch vullen ze elkaar mooi aan.

Laten we zeggen dat we de versies van de entitymodule en daomodule Maven artefacten. We kunnen dit eenvoudig doen zonder de afhankelijkheden in de module-descriptor te hoeven wijzigen. Maven zorgt ervoor dat we de juiste artefacten voor ons opnemen.

Evenzo kunnen we de service-implementatie die de module biedt wijzigen door het "Voorziet .. met" richtlijn in de module descriptor.

We winnen veel als we Maven- en Java-modules samen gebruiken. De eerste biedt de functionaliteit van automatisch, gecentraliseerd afhankelijkheidsbeheer, terwijl de laatste de intrinsieke voordelen van modulariteit biedt.

4.4. De mainappmodule Maven-module

Bovendien moeten we de Maven-module definiëren die de hoofdklasse van het project bevat.

Laten we, zoals we eerder hebben gedaan, het mainappmodule / src / main / java / mainapp directory structuur onder de root directory, en voeg hier het volgende aan toe Toepassing klasse:

openbare klasse Application {public static void main (String [] args) {Map users = new HashMap (); users.put (1, nieuwe gebruiker ("Julie")); users.put (2, nieuwe gebruiker ("David")); Dao userão = nieuwe Userão (gebruikers); userbao.findAll (). forEach (System.out :: println); }}

De Toepassing klas hoofd() methode is vrij eenvoudig. Ten eerste vult het een Hash kaart met een paar Gebruiker voorwerpen. Vervolgens gebruikt het een Gebruiker bijvoorbeeld om ze op te halen uit de Kaart, en vervolgens worden ze weergegeven op de console.

Daarnaast moeten we ook de module's definiëren pom.xml het dossier:

 // bovenliggende coördinaten com.baeldung.mainappmodule mainappmodule 1.0 jar mainappmodule com.baeldung.entitymodule entitymodule 1.0 com.baeldung.daomodule daomodule 1.0 com.baeldung.userdaomodule userdaomodule 1.0 

De afhankelijkheden van de module spreken voor zich. We hoeven dus alleen de module in een Java-module te plaatsen. Daarom onder de mainappmodule / src / main / java directory structuur, laten we de module descriptor opnemen:

module com.baeldung.mainappmodule {vereist com.baeldung.entitypmodule; vereist com.baeldung.userdaopmodule; vereist com.baeldung.daopmodule; gebruikt com.baeldung.daopmodule.Dao; } 

Laten we tot slot deze module toevoegen aan de bovenliggende POM:

 entitymodule daomodule userdaomodule mainappmodule 

Nu alle onderliggende Maven-modules al aanwezig zijn en netjes zijn ingekapseld in Java-modules, ziet de structuur van het project er als volgt uit:

multimodulemavenproject (de hoofdmap) pom.xml | - entitymodule | - src | - main | - java module-info.java | - com | - baeldung | - entiteit User.class pom.xml | - daomodule | - src | - main | - java module-info.java | - com | - baeldung | - dao Dao.class pom.xml | - userdaomodule | - src | - main | - java module-info.java | - com | - baeldung | - userdao Userbao.class pom.xml | - mainappmodule | - src | - main | - java module-info.java | - com | - baeldung | - mainapp Application.class pom.xml 

5. De toepassing uitvoeren

Laten we tot slot de applicatie uitvoeren, ofwel vanuit onze IDE of vanaf een console.

Zoals we zouden verwachten, zouden we er een paar moeten zien Gebruiker objecten die naar de console worden afgedrukt wanneer de toepassing opstart:

Gebruiker {name = Julie} Gebruiker {name = David} 

6. Conclusie

In deze tutorial hebben we op een pragmatische manier geleerd hoe u Maven en het JPMS naast elkaar kunt laten werken bij de ontwikkeling van een eenvoudig Maven-project met meerdere modules dat gebruikmaakt van Java-modules.

Zoals gewoonlijk zijn alle codevoorbeelden die in deze tutorial worden getoond, beschikbaar op GitHub.