«1. Обзор

Система Java Platform Module System (JPMS) обеспечивает большую надежность, лучшее разделение задач и более надежную инкапсуляцию в Java-приложениях. Однако это не инструмент сборки, поэтому ему не хватает возможности автоматического управления зависимостями проекта.

Конечно, мы можем задаться вопросом, можем ли мы использовать хорошо зарекомендовавшие себя инструменты сборки, такие как Maven или Gradle, в модульных приложениях.

На самом деле, мы можем! В этом руководстве мы узнаем, как создать многомодульное приложение Maven с использованием модулей Java.

2. Инкапсуляция модулей Maven в модули Java

Поскольку модульность и управление зависимостями не являются взаимоисключающими понятиями в Java, мы можем легко интегрировать JPMS, например, с Maven, используя лучшее из обоих миров.

В стандартный многомодульный проект Maven мы добавляем один или несколько дочерних модулей Maven, помещая их в корневую папку проекта и объявляя их в родительском POM в разделе \u003cmodules\u003e.

В свою очередь, мы редактируем POM каждого дочернего модуля и указываем его зависимости через стандартные координаты \u003cgroupId\u003e, \u003cartifactId\u003e и \u003cversion\u003e.

Механизм реактора в Maven, отвечающий за работу с многомодульными проектами, заботится о построении всего проекта в правильном порядке.

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

3. Родительский модуль Maven

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

Чтобы упростить код, мы будем использовать обычную карту в качестве базовой структуры данных для хранения объектов предметной области. Конечно, в дальнейшем мы можем легко перейти на полноценную реляционную базу данных.

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

<groupId>com.baeldung.multimodulemavenproject</groupId>
<artifactId>multimodulemavenproject</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<name>multimodulemavenproject</name>
 
<build>
    <pluginManagement>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>
 
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

Есть несколько деталей, которые стоит отметить в определение родительского POM.

Во-первых, поскольку мы используем Java 11, нам потребуется по крайней мере Maven 3.5.0 в нашей системе, так как Maven поддерживает Java 9 и выше, начиная с этой версии.

И нам также понадобится как минимум версия 3.8.0 плагина компилятора Maven. Поэтому давайте обязательно проверим последнюю версию плагина на Maven Central.

4. Дочерние модули Maven

Обратите внимание, что до этого момента родительский POM не объявлял никаких дочерних модулей.

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

  1. entitymodule: will contain a simple domain class
  2. daomodule: will hold the interface required for accessing the persistence layer (a basic DAO contract)
  3. userdaomodule: will include an implementation of the daomodule‘s interface
  4. mainappmodule: the project’s entry point

4.1. Модуль Maven entitymodule

Теперь давайте добавим первый дочерний модуль Maven, который включает только базовый класс предметной области.

В корневом каталоге проекта давайте создадим структуру каталогов entitymodule/src/main/java/com/baeldung/entity и добавим класс User:

public class User {

    private final String name;

    // standard constructor / getter / toString

}

Затем давайте подключим файл pom.xml модуля:

<parent>
    <groupId>com.baeldung.multimodulemavenproject</groupId>
    <artifactId>multimodulemavenproject</artifactId>
    <version>1.0</version>
</parent>
 
<groupId>com.baeldung.entitymodule</groupId>
<artifactId>entitymodule</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>entitymodule</name>

Как мы видим, модуль Entity не имеет никаких зависимостей от других модулей и не требует дополнительных артефактов Maven, так как включает только класс User.

Теперь нам нужно инкапсулировать модуль Maven в модуль Java. Для этого просто поместим следующий файл дескриптора модуля (module-info.java) в каталог entitymodule/src/main/java:

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

Наконец, добавим дочерний модуль Maven в родительский POM: ~ ~~

<modules>
    <module>entitymodule</module>
</modules>

4.2. Модуль daomodule Maven

Давайте создадим новый модуль Maven, который будет содержать простой интерфейс. Это удобно для определения абстрактного контракта для выборки универсальных типов из уровня постоянства.

«На самом деле есть очень веская причина поместить этот интерфейс в отдельный модуль Java. Таким образом, у нас есть абстрактный контракт с высокой степенью развязки, который легко использовать повторно в разных контекстах. По сути, это альтернативная реализация принципа инверсии зависимостей, обеспечивающая более гибкий дизайн.

Поэтому давайте создадим структуру каталогов daomodule/src/main/java/com/baeldung/dao в корневом каталоге проекта и добавим в нее интерфейс Dao\u003cT\u003e:

public interface Dao<T> {

    Optional<T> findById(int id);

    List<T> findAll();

}

Теперь давайте определим файл модуля pom.xml:

<parent>
    // parent coordinates
</parent>
 
<groupId>com.baeldung.daomodule</groupId>
<artifactId>daomodule</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>daomodule</name>

Новый модуль также не требует других модулей или артефактов, поэтому мы просто завернем его в модуль Java. Давайте создадим дескриптор модуля в каталоге daomodule/src/main/java:

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

Наконец, добавим модуль в родительский POM:

<modules>
    <module>entitymodule</module>
    <module>daomodule</module>
</modules>

4.3. Модуль Maven userdaomodule

Далее давайте определим модуль Maven, содержащий реализацию интерфейса Dao.

В корневом каталоге проекта создадим структуру каталогов userdaomodule/src/main/java/com/baeldung/userdao и добавим в нее следующий класс UserDao:

public class UserDao implements Dao<User> {

    private final Map<Integer, User> users;

    // standard constructor

    @Override
    public Optional<User> findById(int id) {
        return Optional.ofNullable(users.get(id));
    }

    @Override
    public List<User> findAll() {
        return new ArrayList<>(users.values());
    }
}

Проще говоря, класс UserDao предоставляет базовый API, который позволяет нам извлекать объекты User из уровня постоянства.

Для простоты мы использовали Map в качестве резервной структуры данных для сохранения объектов предметной области. Конечно, можно обеспечить более тщательную реализацию, использующую, например, диспетчер сущностей Hibernate.

Теперь давайте определим POM модуля Maven:

<parent>
    // parent coordinates
</parent>
 
<groupId>com.baeldung.userdaomodule</groupId>
<artifactId>userdaomodule</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>userdaomodule</name>
 
<dependencies>
    <dependency>
        <groupId>com.baeldung.entitymodule</groupId>
        <artifactId>entitymodule</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>com.baeldung.daomodule</groupId>
        <artifactId>daomodule</artifactId>
        <version>1.0</version>
    </dependency>
</dependencies>

В этом случае все немного по-другому, так как для модуля userdaomodule требуются модули entitymodule и daomodule. Вот почему мы добавили их как зависимости в файл pom.xml.

Нам все еще нужно инкапсулировать этот модуль Maven в модуль Java. Итак, давайте добавим следующий дескриптор модуля в каталог userdaomodule/src/main/java:

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

Наконец, нам нужно добавить этот новый модуль в родительский POM:

<modules>
    <module>entitymodule</module>
    <module>daomodule</module>
    <module>userdaomodule</module>
</modules>

Из высокоуровневого легко увидеть, что файл pom.xml и дескриптор модуля играют разные роли. Тем не менее, они прекрасно дополняют друг друга.

Допустим, нам нужно обновить версии артефактов entitymodule и daomodule Maven. Мы можем легко сделать это, не изменяя зависимости в дескрипторе модуля. Maven позаботится о том, чтобы включить для нас нужные артефакты.

Точно так же мы можем изменить реализацию службы, которую предоставляет модуль, изменив директиву «provides..with» в дескрипторе модуля.

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

4.4. Модуль Maven mainappmodule

Кроме того, нам нужно определить модуль Maven, который содержит основной класс проекта.

Как и раньше, давайте создадим структуру каталогов mainappmodule/src/main/java/mainapp в корневом каталоге и добавим в нее следующий класс приложения:

public class Application {
    
    public static void main(String[] args) {
        Map<Integer, User> 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);
    }   
}

Метод main() класса приложения: довольно просто. Во-первых, он заполняет HashMap парой объектов User. Затем он использует экземпляр UserDao для извлечения их из карты, а затем отображает их на консоли.

Кроме того, нам также необходимо определить файл pom.xml модуля:

<parent>
    // parent coordinates
</parent>
 
<groupId>com.baeldung.mainappmodule</groupId>
<artifactId>mainappmodule</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<name>mainappmodule</name>
 
<dependencies>
    <dependency>
        <groupId>com.baeldung.entitymodule</groupId>
         <artifactId>entitymodule</artifactId>
         <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>com.baeldung.daomodule</groupId>
        <artifactId>daomodule</artifactId>
        <version>1.0</version>
    </dependency>
    <dependency>
        <groupId>com.baeldung.userdaomodule</groupId>
        <artifactId>userdaomodule</artifactId>
        <version>1.0</version>
    </dependency>
</dependencies>

Зависимости модуля говорят сами за себя. Итак, нам просто нужно поместить модуль внутри модуля Java. Поэтому в структуре каталогов mainappmodule/src/main/java давайте включим дескриптор модуля:

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

Наконец, давайте добавим этот модуль в родительский POM:

<modules>
    <module>entitymodule</module>
    <module>daomodule</module>
    <module>userdaomodule</module>
    <module>mainappmodule</module>
</modules>

Со всеми дочерними модулями Maven уже на месте и аккуратно инкапсулированы в модули Java, вот как выглядит структура проекта:

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. Запуск приложения

Наконец, давайте запустим приложение либо из нашей IDE, либо из консоли.

«Как и следовало ожидать, мы должны увидеть пару объектов User, выводимых на консоль при запуске приложения:

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

6. Заключение

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

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