Рубрики
Без рубрики

Многомодульное приложение Maven с модулями Java

Узнайте, как создать многомодульное приложение Maven с помощью системы модулей платформы Java (JPMS).

Автор оригинала: Alejandro Ugarte.

1. Обзор

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

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

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

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

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

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

В свою очередь, мы редактируем POM каждого дочернего модуля и указываем его зависимости с помощью стандартных координат < groupId> , < artifactId> и < version> .

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

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

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

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

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

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

com.baeldung.multimodulemavenproject
multimodulemavenproject
1.0
pom
multimodulemavenproject
 

    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.8.0
                
                    11
                    11
                
            
        
    

 

    UTF-8

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

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

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

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

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

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

  1. модуль сущности : будет содержать простой доменный класс
  2. модуль dao : будет содержать интерфейс, необходимый для доступа к уровню персистентности (базовый DAO контракт)
  3. модуль userdao : будет включать реализацию интерфейса модуля dao
  4. основной модуль приложения : точка входа в проект

4.1. Модуль entitymodule Maven

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

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

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 ) в каталоге entity module/src/main/java :

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

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


    entitymodule

4.2. Модуль daomodule Maven

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

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

Поэтому давайте создадим структуру каталогов dao module/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. Давайте создадим дескриптор модуля в каталоге dao module/src/main/java :

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

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


    entitymodule
    daomodule

4.3. Модуль Maven userdaomodule

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

В корневом каталоге проекта давайте создадим структуру каталогов userdaomodule/src/main/java/com/baeldung/userdao и добавим в нее следующий класс 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());
    }
}

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

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

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


    // parent coordinates

 
com.baeldung.userdaomodule
userdaomodule
1.0
jar
userdaomodule
 

    
        com.baeldung.entitymodule
        entitymodule
        1.0
    
    
        com.baeldung.daomodule
        daomodule
        1.0
    

В этом случае все немного по-другому, так как модуль userdao требует модуля e nity и модуля dao модулей. Вот почему мы добавили их в качестве зависимостей в pom.xml файл.

Нам все еще нужно инкапсулировать этот модуль Maven в модуль Java. Итак, давайте добавим следующий дескриптор модуля в каталог userdao module/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:


    entitymodule
    daomodule
    userdaomodule

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

Допустим, нам нужно обновить версии артефактов e n tity module и dao module Maven. Мы можем легко сделать это без необходимости изменять зависимости в дескрипторе модуля. Maven позаботится о том, чтобы включить нужные артефакты для нас.

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

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

4.4. Основной модуль приложения Модуль Maven

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

Как и раньше, давайте создадим main app module/src/main/java/main app структуру каталогов в корневом каталоге и добавим в нее следующий Application класс:

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

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

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


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

Зависимости модуля довольно понятны. Итак, нам просто нужно поместить модуль внутри модуля Java. Поэтому в структуре каталогов main app module/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:


    entitymodule
    daomodule
    userdaomodule
    mainappmodule

Поскольку все дочерние модули 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. Заключение

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

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