Applicazione Maven multi-modulo con moduli Java

1. Panoramica

Il Java Platform Module System (JPMS) aggiunge più affidabilità, una migliore separazione delle preoccupazioni e un incapsulamento più forte alle applicazioni Java. Tuttavia, non è uno strumento di compilazione, quindi non ha la capacità di gestire automaticamente le dipendenze del progetto.

Ovviamente, potremmo chiederci se possiamo utilizzare strumenti di compilazione consolidati, come Maven o Gradle , in applicazioni modulari.

In realtà, possiamo! In questo tutorial impareremo come creare un'applicazione Maven multi-modulo utilizzando i moduli Java .

2. Incapsulamento di moduli Maven in moduli Java

Poiché la modularità e la gestione delle dipendenze non sono concetti che si escludono a vicenda in Java, possiamo integrare perfettamente JPMS, ad esempio, con Maven, sfruttando così il meglio di entrambi i mondi.

In un progetto Maven multi-modulo standard, aggiungiamo uno o più moduli Maven figlio posizionandoli nella cartella principale del progetto e dichiarandoli nel POM padre, all'interno del sezione.

A sua volta, modifichiamo il POM di ogni modulo figlio e ne specifichiamo le dipendenze tramite le coordinate standard < groupId> , < artifactId> e < version> .

Il meccanismo del reattore di Maven, responsabile della gestione dei progetti multimodulo, si occupa di costruire l'intero progetto nel giusto ordine.

In questo caso, useremo fondamentalmente la stessa metodologia di progettazione, ma con una variante sottile ma fondamentale: avvolgeremo ogni modulo Maven in un modulo Java aggiungendo ad esso il file descrittore del modulo , module-info.java .

3. Il modulo Maven genitore

Per dimostrare come la modularità e la gestione delle dipendenze funzionano alla grande insieme, costruiremo un progetto Maven multi-modulo demo di base, le cui funzionalità saranno limitate al solo recupero di alcuni oggetti di dominio da un livello di persistenza .

Per mantenere il codice semplice, useremo una semplice mappa come struttura dati sottostante per l'archiviazione degli oggetti del dominio. Naturalmente, possiamo facilmente passare più avanti lungo la strada a un database relazionale a tutti gli effetti.

Cominciamo definendo il modulo genitore Maven. Per fare ciò, creiamo una directory del progetto radice chiamata, ad esempio, multimodulemavenproject (ma potrebbe essere qualsiasi altra cosa) e aggiungiamo ad essa il file genitore pom.xml :

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

Ci sono alcuni dettagli che vale la pena notare nella definizione del POM genitore.

Prima di tutto, poiché stiamo utilizzando Java 11, avremo bisogno almeno di Maven 3.5.0 sul nostro sistema , poiché Maven supporta Java 9 e versioni successive da quella versione in poi .

E avremo anche bisogno almeno della versione 3.8.0 del plugin del compilatore Maven. Pertanto, assicuriamoci di controllare l'ultima versione del plug-in su Maven Central.

4. I moduli Child Maven

Si noti che fino a questo punto, il POM padre non dichiara alcun modulo figlio .

Poiché il nostro progetto demo recupererà alcuni oggetti di dominio dal livello di persistenza, creeremo quattro moduli Maven figlio:

  1. entitymodule : conterrà una semplice classe di dominio
  2. daomodule : manterrà l'interfaccia richiesta per accedere al livello di persistenza (un contratto DAO di base)
  3. userdaomodule includerà un'implementazione del: daomodule interfaccia 's
  4. mainappmodule : il punto di ingresso del progetto

4.1. Il modulo Maven entitymodule

Ora aggiungiamo il primo modulo figlio Maven, che include solo una classe di dominio di base.

Nella directory root del progetto, creiamo la struttura di directory entitymodule / src / main / java / com / baeldung / entity e aggiungiamo una classe User :

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

Successivamente, includiamo il file pom.xml del modulo :

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

Come possiamo vedere, il modulo Entity non ha dipendenze da altri moduli, né richiede artefatti Maven aggiuntivi, poiché include solo la classe User .

Ora, dobbiamo incapsulare il modulo Maven in un modulo Java . Per ottenere ciò, posizioniamo semplicemente il seguente file descrittore del modulo ( module-info.java ) nella directory entitymodule / src / main / java :

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

Infine, aggiungiamo il modulo Maven figlio al POM genitore:

 entitymodule 

4.2. Il modulo Maven daomodule

Creiamo un nuovo modulo Maven che conterrà una semplice interfaccia. Ciò è utile per definire un contratto astratto per il recupero di tipi generici dal livello di persistenza.

È un dato di fatto, c'è una ragione molto convincente per inserire questa interfaccia in un modulo Java separato. In questo modo, abbiamo un contratto astratto, altamente disaccoppiato, che è facile da riutilizzare in contesti diversi. Al centro, questa è un'implementazione alternativa del principio di inversione delle dipendenze, che produce un design più flessibile.

Pertanto, creiamo la struttura della directory daomodule / src / main / java / com / baeldung / dao sotto la directory principale del progetto e aggiungiamo ad essa l' interfaccia Dao :

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

Ora definiamo il file pom.xml del modulo :

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

Il nuovo modulo non richiede nemmeno altri moduli o artefatti, quindi lo avvolgeremo semplicemente in un modulo Java. Creiamo il descrittore del modulo nella directory daomodule / src / main / java :

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

Infine, aggiungiamo il modulo al POM genitore:

 entitymodule daomodule  

4.3. Il modulo Maven userdaomodule

Successivamente, definiamo il modulo Maven che contiene un'implementazione dell'interfaccia Dao .

Nella directory principale del progetto, creiamo la struttura della directory userdaomodule / src / main / java / com / baeldung / userdao e aggiungiamo ad essa la seguente classe UserDao :

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()); } }

In poche parole, la classe UserDao fornisce un'API di base che ci consente di recuperare gli oggetti utente dal livello di persistenza.

Per semplificare le cose, abbiamo utilizzato una mappa come struttura dati di supporto per la persistenza degli oggetti del dominio. Naturalmente, è possibile fornire un'implementazione più completa che utilizza, ad esempio, l'entità manager di Hibernate.

Ora, definiamo il POM del modulo Maven:

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

In questo tutorial abbiamo imparato in modo pragmatico come far lavorare fianco a fianco Maven e JPMS, nello sviluppo di un progetto Maven multi-modulo di base che utilizza moduli Java .

Come al solito, tutti gli esempi di codice mostrati in questo tutorial sono disponibili su GitHub.