1. 概述

Java 平台模块系统(JPMS)从 Java 9 开始引入,为 Java 应用带来了更好的可靠性、更清晰的关注点分离以及更强的封装性。但 JPMS 本身不是一个构建工具,它不具备自动管理项目依赖的能力

那我们自然会想:是否可以在模块化项目中继续使用 Maven 或 Gradle 这类成熟的构建工具?

答案是肯定的。本文将通过一个完整的示例,演示如何在 Maven 多模块项目中使用 Java 模块(Java Module),实现模块化与依赖管理的完美结合。


2. Maven 模块与 Java 模块的结合

Maven 的多模块项目通过 parent POM 定义多个子模块,并通过 reactor 机制控制构建顺序。而 Java 模块则通过 module-info.java 实现模块封装与依赖声明。

两者并不冲突,我们可以将每个 Maven 模块封装成一个 Java 模块,从而在享受 Maven 依赖管理的同时,获得模块化带来的封装性优势。

关键点:

  • 每个 Maven 模块对应一个 Java 模块
  • 使用 module-info.java 定义模块依赖和导出包
  • Maven 负责构建顺序与依赖管理,Java 模块负责运行时隔离

3. 父级 Maven 模块配置

我们创建一个名为 multimodulemavenproject 的根目录作为项目基础结构。

父级 POM(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.12.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
        </plugins>
    </pluginManagement>
</build>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

⚠️ 注意:

  • 使用 Java 17 需要 Maven ≥ 3.5.0
  • maven-compiler-plugin ≥ 3.8.0 才支持 Java 17+

4. 子模块定义与封装

我们构建如下四个 Maven 子模块:

  1. entitymodule: 定义领域对象 User
  2. daomodule: 定义 DAO 接口
  3. userdaomodule: 实现 DAO 接口
  4. mainappmodule: 主程序入口

4.1 entitymodule 模块

结构如下:

entitymodule
└── src
    └── main
        └── java
            ├── module-info.java
            └── com
                └── baeldung
                    └── entity
                        User.java

User.java 示例代码:

public class User {
    private final String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "User{name='" + name + "'}";
    }
}

module-info.java

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

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>

父 POM 添加模块声明:

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

4.2 daomodule 模块

结构如下:

daomodule
└── src
    └── main
        └── java
            ├── module-info.java
            └── com
                └── baeldung
                    └── dao
                        Dao.java

Dao.java 示例代码:

import java.util.List;
import java.util.Optional;

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

module-info.java

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

pom.xml

<parent>
    <!-- 父级坐标 -->
</parent>

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

父 POM 添加模块声明:

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

4.3 userdaomodule 模块

结构如下:

userdaomodule
└── src
    └── main
        └── java
            ├── module-info.java
            └── com
                └── baeldung
                    └── userdao
                        UserDao.java

UserDao.java 示例代码:

import java.util.*;

public class UserDao implements Dao<User> {
    private final Map<Integer, User> users;

    public UserDao(Map<Integer, User> users) {
        this.users = users;
    }

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

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

module-info.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.xml

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

父 POM 添加模块声明:

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

4.4 mainappmodule 模块

结构如下:

mainappmodule
└── src
    └── main
        └── java
            ├── module-info.java
            └── com
                └── baeldung
                    └── mainapp
                        Application.java

Application.java 示例代码:

import java.util.*;
import com.baeldung.dao.Dao;
import com.baeldung.userdao.UserDao;
import com.baeldung.entity.User;

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<User> userDao = new UserDao(users);
        userDao.findAll().forEach(System.out::println);
    }
}

module-info.java

module com.baeldung.mainappmodule {
    requires com.baeldung.entitymodule;
    requires com.baeldung.daomodule;
    requires com.baeldung.userdaomodule;
    uses com.baeldung.daomodule.Dao;
}

pom.xml

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

父 POM 添加模块声明:

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

5. 项目结构总览

最终项目结构如下:

multimodulemavenproject
├── pom.xml
├── entitymodule
│   ├── pom.xml
│   └── src/main/java
│       ├── module-info.java
│       └── com/baeldung/entity/User.java
├── daomodule
│   ├── pom.xml
│   └── src/main/java
│       ├── module-info.java
│       └── com/baeldung/dao/Dao.java
├── userdaomodule
│   ├── pom.xml
│   └── src/main/java
│       ├── module-info.java
│       └── com/baeldung/userdao/UserDao.java
└── mainappmodule
    ├── pom.xml
    └── src/main/java
        ├── module-info.java
        └── com/baeldung/mainapp/Application.java

6. 运行应用

在命令行或 IDE 中运行 mainappmodule 模块,输出如下:

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

7. 小结

本文通过一个完整的 Maven 多模块项目,演示了如何结合 Java 模块系统(JPMS)进行开发。关键点总结如下:

✅ Maven 负责构建与依赖管理
✅ Java 模块提供运行时封装与模块化结构
✅ 每个 Maven 模块封装为一个 Java 模块,结构清晰、职责明确
module-info.javapom.xml 各司其职,互不干扰

如果你希望在大型项目中提升模块化程度与依赖管理能力,Maven + Java 模块是值得尝试的组合方案

完整代码可在 GitHub 获取。


原始标题:Multi-Module Maven Application with Java Modules | Baeldung