Багатомодульний додаток Maven з модулями Java

1. Огляд

Модульна система платформи Java (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 та додамо клас користувача :

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.