背景

本文是《Java 后端从小白到大神》修仙系列第二十篇,正式进入Java后端世界,本篇文章主要聊Java基础。若想详细学习请点击首篇博文,我们开始把。

文章概览

  1. Java 9+ 模块化编程

Java 9+ 模块化编程(JPMS)

1. 模块化概念

Java 模块化是 Java 9 引入的核心特性(Project Jigsaw),旨在解决大型项目中代码依赖混乱、JAR 文件臃肿、类路径冲突等问题。模块化通过 module-info.java 文件定义模块的边界,明确以下内容:

  • 模块名称:唯一标识模块,模块目录名必须与模块名完全一致
  • 导出的包:允许其他模块访问的包(exports)
  • 依赖的模块:声明需要哪些模块(requires)
  • 开放反射的包:允许其他模块通过反射访问(opens)
  • 提供的服务:声明服务提供者或消费者(uses/provides)

模块化的核心目标是 强封装性 和 显式依赖管理。

2. 模块化编程

1. 手动创建

1. 创建项目根目录结构
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 项目根目录(非模块化根,仅用于组织)
mkdir my-modular-project && cd my-modular-project

# 创建模块1:com.example.greeting
mkdir -p com.example.greeting/src/com/example/greeting
new-item com.example.greeting/module-info.java
new-item com.example.greeting/src/com/example/greeting/GreetingService.java

# 创建模块2:com.example.app
mkdir -p com.example.app/src/com/example/app
new-item com.example.app/module-info.java
new-item com.example.app/src/com/example/app/Main.java
2. 编写模块描述文件
1
2
3
4
5
6
7
8
9
// com.example.greeting/module-info.java
module com.example.greeting {
    exports com.example.greeting;
}

// com.example.app/module-info.java
module com.example.app {
    requires com.example.greeting;
}
3. 编写代码文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// com.example.greeting/src/com/example/greeting/GreetingService.java
package com.example.greeting;

public class GreetingService {
    public static String greet(String name) {
        return "Hello, " + name + "!";
    }
}

// com.example.app/src/com/example/app/Main.java
package com.example.app;

import com.example.greeting.GreetingService;

public class Main {
    public static void main(String[] args) {
        System.out.println(GreetingService.greet("Java Modules"));
    }
}
4. 编译与运行
1
2
3
4
5
6
7
8
# 编译所有模块
javac -d out \
      --module-source-path ".;com.example.greeting/src;com.example.app/src" \
      --module com.example.greeting,com.example.app

# 运行主模块
java --module-path out \
     --module com.example.app/com.example.app.Main

2. 使用 Maven 创建(推荐生产环境)

1. 创建父项目(聚合模块)
1
2
3
4
5
mvn archetype:generate \
    -DgroupId=com.example \
    -DartifactId=my-modular-project \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false
2. 修改父项目为 pom 类型
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- my-modular-project/pom.xml -->
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>my-modular-project</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>pom</packaging> <!-- 关键:父模块使用 "pom" 作为打包方式 -->

    <modules>
        <module>com.example.greeting</module> <!-- 先注释该行,子模块创建完毕再打开,否则报错 -->
        <module>com.example.app</module> <!-- 先注释该行,子模块创建完毕再打开,否则报错 -->
    </modules>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.example</groupId>
                <artifactId>com.example.greeting</artifactId>
                <version>${project.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>
3. 创建子模块

进入父项目根目录下,使用 Maven 生成子模块。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 创建 greeting 模块
mvn archetype:generate \
    -DgroupId=com.example \
    -DartifactId=com.example.greeting \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false

# 创建 app 模块
mvn archetype:generate \
    -DgroupId=com.example \
    -DartifactId=com.example.app \
    -DarchetypeArtifactId=maven-archetype-quickstart \
    -DinteractiveMode=false

修改子模块的 pom.xml 文件

子模块 APP POM文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>my-modular-project</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>com.example.app</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>com.example.greeting</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 升级编译器插件版本并配置模块化参数 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <release>21</release> <!-- 使用 release 参数替代 source/target -->
                    <compilerArgs>
                        <!-- 关键修正:指向本地仓库的模块路径 -->
                        <arg>--module-path</arg>
                        <arg>
                            ${settings.localRepository}/com/example/com.example.greeting/${project.version}</arg> <!-- 动态获取本地仓库路径(无需硬编码) -->
                        <arg>--add-modules</arg> <!-- 显式添加依赖模块到编译路径 -->
                        <arg>com.example.greeting</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
子模块 GREETING POM文件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.example</groupId>
        <artifactId>my-modular-project</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>com.example.greeting</artifactId>

    <dependencies>
        <!-- 这里可以添加其他依赖 -->
    </dependencies>
</project>

子模块的 类 文件

子模块 APP 类文件
1
2
3
4
5
com.example.app\src\main\java\module-info.java路径位置

module com.example.app {
    requires com.example.greeting;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package com.example.app;

import com.example.greeting.GreetingService;

public class Main {
    public static void main(String[] args) {
        GreetingService service = new GreetingService();
        System.out.println(service.getGreeting());
    }
}
子模块 GREETING 类文件
1
2
3
4
5
com.example.greeting\src\main\java\module-info.java注路径位置

module com.example.greeting {
    exports com.example.greeting; 
}
1
2
3
4
5
6
7
package com.example.greeting;

public class GreetingService {
    public String getGreeting() {
        return "Hello, Maven Modules!";
    }
}
4. 最终目录结构
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
my-modular-project/
├── pom.xml
├── com.example.greeting
│   ├── src
│   │   └── main
│   │       └── java
│   │           ├── com/example/greeting/GreetingService.java
│   │           └── module-info.java
│   └── pom.xml
└── com.example.app
    ├── src
    │   └── main
    │       └── java
    │           ├── com/example/app/Main.java
    │           └── module-info.java
    └── pom.xml
5. 编译并运行

cd my-modular-project
mvn clean install

1
2
使用 mvn exec:java(推荐)
mvn exec:java -pl com.example.app -Dexec.mainClass=com.example.app.Main
  • -pl com.example.app 只运行 com.example.app 这个模块。
  • -Dexec.mainClass=com.example.app.Main 指定 Main 入口类。
1
2
3
使用 java --module-path 运行
java --module-path com.example.greeting/target/com.example.greeting-1.0-SNAPSHOT.jar;com.example.app/target/com.example.app-1.0-SNAPSHOT.jar \
     --module com.example.app/com.example.app.Main
  • –module-path 指定模块路径(包括 com.example.greeting 和 com.example.app)。
  • –module com.example.app/com.example.app.Main 指定运行 Main 类。

那么正确运行后,输出应该是: Hello, Modular Java!

总结

Java 9+ 模块化编程(JPMS) vs. Maven 多模块项目管理。Java 9+ 模块化 (JPMS),是语言层面的模块化系统(Java Platform Module System),强封装性、明确的模块依赖关系、解决 JAR 地狱,代码结构层面(编译和运行时生效)。Maven 多模块项目,是项目构建工具的模块化管理(依赖和构建流程控制),代码组织、依赖复用、多模块协同构建,项目工程化层面(构建时生效)。