Мултимодулно приложение Maven с Java модули

1. Общ преглед

Java Platform Module System (JPMS) добавя повече надеждност, по-добро разделяне на проблемите и по-силно капсулиране на Java приложения. Това обаче не е инструмент за изграждане, поради което му липсва възможност за автоматично управление на зависимостите на проекта.

Разбира се, можем да се запитаме дали можем да използваме утвърдени инструменти за изграждане, като Maven или Gradle , в модулирани приложения.

Всъщност можем! В този урок ще научим как да създадем многомодулно приложение Maven с помощта на Java модули .

2. Капсулиране на модули Maven в модули Java

Тъй като модулността и управлението на зависимостите не са взаимно изключващи се понятия в Java, ние можем безпроблемно да интегрираме JPMS, например, с Maven, като по този начин използваме най-доброто от двата свята.

В стандартен многомодулен проект Maven, ние добавяме един или повече дъщерни модули Maven, като ги поставяме под основната папка на проекта и ги декларираме в родителския POM, в рамките на раздел.

На свой ред, ние редактираме POM на всеки детски модул и определяме неговите зависимости чрез стандартните координати < groupId> , < artifactId> и < version> .

В реактор механизъм в Maven - отговаря за обработката на няколко модула проекти - се грижи за изграждането на целия проект в правилния ред.

В този случай ние по принцип ще използваме същата методология на проектиране, но с един фин, но основен вариант: ще увием всеки модул Maven в модул Java, като добавим към него файла с дескриптора на модула , module-info.java .

3. Модулът Parent Maven

За да демонстрираме как модулността и управлението на зависимостите работят чудесно заедно, ще изградим основен демонстрационен мултимодулен проект Maven, чиято функционалност ще бъде стеснена до извличане на някои обекти на домейна от персистиращ слой .

За да улесним кода, ще използваме обикновена карта като основна структура от данни за съхраняване на обектите на домейна. Разбира се, можем лесно да преминем по-надолу към пълноценна релационна база данни.

Нека започнем с дефиниране на родителския модул Maven. За да постигнем това, нека създадем основна директория на проекта, наречена, например, multimodulemavenproject (но може да е нещо друго), и добавете към нея родителския файл pom.xml :

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

Има няколко подробности, които си струва да се отбележат в дефиницията на родителския POM.

Първо, тъй като използваме Java 11, ще се нуждаем от поне Maven 3.5.0 в нашата система , тъй като Maven поддържа Java 9 и по-нова версия от тази версия нататък .

Освен това ще ни трябва поне версия 3.8.0 на приставката за компилатор Maven. Ето защо, нека не забравяме да проверим най-новата версия на приставката на Maven Central.

4. Модулите Child Maven

Забележете, че до този момент родителският POM не декларира дъщерни модули .

Тъй като нашият демонстрационен проект ще извлече някои обектни домейни от персистиращия слой, ще създадем четири дъщерни модула Maven:

  1. entitymodule : ще съдържа прост клас на домейн
  2. daomodule : ще съдържа интерфейса, необходим за достъп до слоя за постоянство (основен DAO договор)
  3. userdaomodule : ще включва изпълнение на интерфейса на daomodule
  4. mainappmodule : входната точка на проекта

4.1. Модулът на субекта Maven

Сега, нека добавим първия подчинен модул Maven, който просто включва основен клас на домейн.

В основната директория на проекта, нека създадем entitymodule / src / main / java / com / baeldung / entity directory структура и добавим потребителски клас:

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

След това нека включим файла pom.xml на модула :

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

Както виждаме, модулът Entity няма никакви зависимости към други модули, нито изисква допълнителни артефакти на Maven, тъй като включва само класа User .

Сега трябва да капсулираме модула Maven в модул Java . За да постигнем това, нека просто поставим следния файл с дескриптор на модула ( module-info.java ) под директория entitymodule / src / main / java :

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

И накрая, нека добавим детския модул Maven към родителския POM:

 entitymodule 

4.2. В daomodule Maven модул

Нека създадем нов модул Maven, който ще съдържа прост интерфейс. Това е удобно за дефиниране на абстрактен договор за извличане на родови типове от персистиращия слой.

Всъщност има много убедителна причина този интерфейс да се постави в отделен Java модул. По този начин имаме абстрактен, силно отделен договор, който е лесен за повторно използване в различен контекст. В основата си това е алтернативно изпълнение на принципа за инверсия на зависимостите, което дава по-гъвкав дизайн.

Затова нека създадем структурата на директориите daomodule / src / main / java / com / baeldung / dao под основната директория на проекта и добавим към нея интерфейса Dao :

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

Сега, нека дефинираме файла pom.xml на модула :

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

Новият модул също не изисква други модули или артефакти, така че просто ще го увием в модул Java. Нека създадем дескриптора на модула под директорията daomodule / src / main / java :

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

И накрая, нека добавим модула към родителския POM:

 entitymodule daomodule  

4.3. Модулът userdaomodule Maven

След това нека дефинираме модула Maven, който съдържа изпълнение на интерфейса 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

В този урок научихме по прагматичен начин как да поставим Maven и JPMS да работят едно до друго при разработването на основен многомодулен проект Maven, който използва Java модули .

Както обикновено, всички примерни кодове, показани в този урок, са достъпни в GitHub.