Application Maven multi-module avec modules Java

1. Vue d'ensemble

Le Java Platform Module System (JPMS) ajoute plus de fiabilité, une meilleure séparation des préoccupations et une encapsulation plus forte aux applications Java. Cependant, ce n'est pas un outil de construction, il n'a donc pas la capacité de gérer automatiquement les dépendances de projet.

Bien sûr, on peut se demander si nous pouvons utiliser des outils de construction bien établis, comme Maven ou Gradle , dans des applications modulaires.

En fait, nous pouvons! Dans ce didacticiel, nous allons apprendre à créer une application Maven multi-module à l'aide de modules Java .

2. Encapsulation de modules Maven dans des modules Java

Puisque la modularité et la gestion des dépendances ne sont pas des concepts mutuellement exclusifs en Java, nous pouvons intégrer de manière transparente le JPMS, par exemple, avec Maven, tirant ainsi parti du meilleur des deux mondes.

Dans un projet Maven multi-module standard, nous ajoutons un ou plusieurs modules Maven enfants en les plaçant sous le dossier racine du projet et en les déclarant dans le POM parent, dans le section.

À notre tour, nous éditons le POM de chaque module enfant et spécifions ses dépendances via les coordonnées standard < groupId> , < artifactId> et < version> .

Le mécanisme du réacteur de Maven - chargé de gérer les projets multi-modules - se charge de construire l'ensemble du projet dans le bon ordre.

Dans ce cas, nous utiliserons essentiellement la même méthodologie de conception, mais avec une variante subtile mais fondamentale: nous allons envelopper chaque module Maven dans un module Java en y ajoutant le fichier descripteur de module , module-info.java .

3. Le module Parent Maven

Pour démontrer comment la modularité et la gestion des dépendances fonctionnent bien ensemble, nous allons créer un projet Maven de démonstration de base à plusieurs modules, dont les fonctionnalités seront limitées à la simple récupération de certains objets de domaine à partir d'une couche de persistance .

Pour garder le code simple, nous utiliserons une carte simple comme structure de données sous-jacente pour stocker les objets de domaine. Bien sûr, nous pouvons facilement passer plus tard à une base de données relationnelle à part entière.

Commençons par définir le module Maven parent. Pour ce faire, créons un répertoire de projet racine appelé, par exemple, multimodulemavenproject (mais cela pourrait être n'importe quoi d'autre), et ajoutons-y le fichier pom.xml parent :

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

Il y a quelques détails à noter dans la définition du POM parent.

Tout d'abord, puisque nous utilisons Java 11, nous aurons besoin d'au moins Maven 3.5.0 sur notre système , car Maven prend en charge Java 9 et plus à partir de cette version .

Et nous aurons également besoin d'au moins la version 3.8.0 du plugin du compilateur Maven. Assurez-vous donc de vérifier la dernière version du plugin sur Maven Central.

4. Les modules Child Maven

Notez que jusqu'à présent, le POM parent ne déclare aucun module enfant .

Étant donné que notre projet de démonstration va récupérer des objets de domaine à partir de la couche de persistance, nous allons créer quatre modules Maven enfants:

  1. entitymodule : contiendra une classe de domaine simple
  2. daomodule : contiendra l'interface requise pour accéder à la couche de persistance (un contrat DAO de base)
  3. userdaomodule : comprendra une mise en œuvre du daomodule l'interface «
  4. mainappmodule : le point d'entrée du projet

4.1. Le module Maven entitymodule

Maintenant, ajoutons le premier module Maven enfant, qui comprend juste une classe de domaine de base.

Sous le répertoire racine du projet, créons la structure de répertoire entitymodule / src / main / java / com / baeldung / entity et ajoutons une classe User :

public class User { private final String name; // standard constructor / getter / toString }

Ensuite, incluons le fichier pom.xml du module :

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

Comme nous pouvons le voir, le module Entity n'a aucune dépendance à d'autres modules, ni ne nécessite d'artefacts Maven supplémentaires, car il inclut uniquement la classe User .

Maintenant, nous devons encapsuler le module Maven dans un module Java . Pour ce faire, plaçons simplement le fichier descripteur de module suivant ( module-info.java ) sous le répertoire entitymodule / src / main / java :

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

Enfin, ajoutons le module Maven enfant au POM parent:

 entitymodule 

4.2. Le module daomodule Maven

Créons un nouveau module Maven qui contiendra une interface simple. Ceci est pratique pour définir un contrat abstrait pour récupérer des types génériques à partir de la couche de persistance.

En fait, il y a une raison très convaincante de placer cette interface dans un module Java séparé. Ce faisant, nous avons un contrat abstrait, hautement découplé, qui est facile à réutiliser dans différents contextes. À la base, il s'agit d'une implémentation alternative du principe d'inversion de dépendance, qui donne une conception plus flexible.

Par conséquent, créons la structure de répertoire daomodule / src / main / java / com / baeldung / dao sous le répertoire racine du projet, et ajoutons-y l' interface Dao :

public interface Dao { Optional findById(int id); List findAll(); }

Maintenant, définissons le fichier pom.xml du module :

 // parent coordinates  com.baeldung.daomodule daomodule 1.0 jar daomodule

Le nouveau module ne nécessite pas non plus d'autres modules ou artefacts, nous allons donc simplement l'envelopper dans un module Java. Créons le descripteur de module sous le répertoire daomodule / src / main / java :

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

Enfin, ajoutons le module au POM parent:

 entitymodule daomodule  

4.3. Le module Maven userdaomodule

Ensuite, définissons le module Maven qui contient une implémentation de l' interface Dao .

Under the project's root directory, let's create the userdaomodule/src/main/java/com/baeldung/userdao directory structure, and add to it the following UserDao class:

public class UserDao implements Dao { private final Map users; // standard constructor @Override public Optional findById(int id) { return Optional.ofNullable(users.get(id)); } @Override public List findAll() { return new ArrayList(users.values()); } }

Simply put, the UserDao class provides a basic API that allows us to fetch User objects from the persistence layer.

To keep things simple, we used a Map as the backing data structure for persisting the domain objects. Of course, it's possible to provide a more thorough implementation that uses, for instance, Hibernate's entity manager.

Now, let's define the Maven module's POM:

 // parent coordinates  com.baeldung.userdaomodule userdaomodule 1.0 jar userdaomodule   com.baeldung.entitymodule entitymodule 1.0   com.baeldung.daomodule daomodule 1.0  

In this case, things are slightly different, as the userdaomodule module requires the entitymodule and daomodule modules. That's why we added them as dependencies in the pom.xml file.

We still need to encapsulate this Maven module into a Java module. So, let's add the following module descriptor under the userdaomodule/src/main/java directory:

module com.baeldung.userdaomodule { requires com.baeldung.entitymodule; requires com.baeldung.daomodule; provides com.baeldung.daomodule.Dao with com.baeldung.userdaomodule.UserDao; exports com.baeldung.userdaomodule; } 

Finally, we need to add this new module to the parent POM:

 entitymodule daomodule userdaomodule 

From a high-level view, it's easy to see that the pom.xml file and the module descriptor play different roles. Even so, they complement each other nicely.

Let's say that we need to update the versions of the entitymodule and daomodule Maven artifacts. We can easily do this without having to change the dependencies in the module descriptor. Maven will take care of including the right artifacts for us.

Similarly, we can change the service implementation that the module provides by modifying the “provides..with” directive in the module descriptor.

We gain a lot when we use Maven and Java modules together. The former brings the functionality of automatic, centralized dependency management, while the latter provides the intrinsic benefits of modularity.

4.4. The mainappmodule Maven Module

Additionally, we need to define the Maven module that contains the project's main class.

As we did before, let's create the mainappmodule/src/main/java/mainapp directory structure under the root directory, and add to it the following Application class:

public class Application { public static void main(String[] args) { Map users = new HashMap(); users.put(1, new User("Julie")); users.put(2, new User("David")); Dao userDao = new UserDao(users); userDao.findAll().forEach(System.out::println); } }

The Application class's main() method is quite simple. First, it populates a HashMap with a couple of User objects. Next, it uses a UserDao instance for fetching them from the Map, and then it displays them to the console.

In addition, we also need to define the module's pom.xml file:

 // parent coordinates  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   

The module's dependencies are pretty self-explanatory. So, we just need to place the module inside a Java module. Therefore, under the mainappmodule/src/main/java directory structure, let's include the module descriptor:

module com.baeldung.mainappmodule { requires com.baeldung.entitypmodule; requires com.baeldung.userdaopmodule; requires com.baeldung.daopmodule; uses com.baeldung.daopmodule.Dao; } 

Finally, let's add this module to the parent POM:

 entitymodule daomodule userdaomodule mainappmodule  

With all the child Maven modules already in place, and neatly encapsulated in Java modules, here's how the project's structure looks:

multimodulemavenproject (the root directory) pom.xml |-- entitymodule |-- src |-- main | -- java module-info.java |-- com |-- baeldung |-- entity 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 UserDao.class pom.xml |-- mainappmodule |-- src |-- main | -- java module-info.java |-- com |-- baeldung |-- mainapp Application.class pom.xml 

5. Running the Application

Finally, let's run the application, either from within our IDE or from a console.

As we might expect, we should see a couple of User objects printed out to the console when the application starts up:

User{name=Julie} User{name=David} 

6. Conclusion

Dans ce tutoriel, nous avons appris de manière pragmatique comment mettre Maven et le JPMS côte à côte, dans le développement d'un projet Maven multi-module de base utilisant des modules Java .

Comme d'habitude, tous les exemples de code présentés dans ce didacticiel sont disponibles à l'adresse over sur GitHub.