CodeGym /课程 /JAVA 25 SELF /多模块项目的构建:Maven、Gradle、IDEA

多模块项目的构建:Maven、Gradle、IDEA

JAVA 25 SELF
第 60 级 , 课程 4
可用

1. 构建模块化项目:基本原则

编译器与模块路径(module-path)

在模块出现之前,整个应用程序是通过所谓的 classpath 编译和运行的——它只是 Java 查找类的目录和 JAR 文件列表。在模块系统中出现了一个新角色——module-path。现在,编译器和 JVM 不仅需要知道类在哪里,还需要知道它们属于哪些模块、有哪些依赖,以及哪些包被导出。

关键点:

  • 对于模块化项目,请使用 --module-path 而不是 -classpath
  • 编译器和 JVM 必须能看到应用所需的所有模块(以及它们的依赖)。

示例:手动编译模块

假设你有两个模块:coreapp,其中 app 依赖 core

目录结构:

project-root/
  core/
    src/
      main/
        java/
          module-info.java
          com/example/core/...
  app/
    src/
      main/
        java/
          module-info.java
          com/example/app/...

手动编译(演示):

# 先编译 core
javac -d out/core core/src/main/java/module-info.java core/src/main/java/com/example/core/*.java

# 然后编译 app,并把 module-path 指向 core
javac --module-path out/core -d out/app app/src/main/java/module-info.java app/src/main/java/com/example/app/*.java

运行:

java --module-path out/core:out/app -m app/com.example.app.Main

在真实项目中无需手动执行这些步骤——请使用 Maven/GradleIDE

2. Maven:构建多模块项目

Maven 中的多模块项目结构

Maven 早已支持包含多个模块(submodules)的结构。随着 Java 模块系统的出现,这些 “模块” 在 Maven 中往往与 Java 模块对应,但并非总是如此:Maven 模块只是一个子项目,而 Java 模块是由 module-info.java 描述的。通常一个 Maven 模块 = 一个 Java 模块。

典型结构:

myproject/
  pom.xml                # 父 POM
  core/
    pom.xml              # 模块 core
    src/main/java/
      module-info.java
      com/example/core/...
  app/
    pom.xml              # 模块 app
    src/main/java/
      module-info.java
      com/example/app/...

Parent POM 示例(myproject/pom.xml):

<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>myproject</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging>
  <modules>
    <module>core</module>
    <module>app</module>
  </modules>
</project>

core 模块的 POM 示例(core/pom.xml):

<project>
  <parent>
    <groupId>com.example</groupId>
    <artifactId>myproject</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <artifactId>core</artifactId>
  <dependencies>
    <!-- 这里可以声明依赖,例如其他模块或第三方库 -->
  </dependencies>
</project>

app 模块的 POM 示例(app/pom.xml):

<project>
  <parent>
    <groupId>com.example</groupId>
    <artifactId>myproject</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>
  <artifactId>app</artifactId>
  <dependencies>
    <dependency>
      <groupId>com.example</groupId>
      <artifactId>core</artifactId>
      <version>1.0-SNAPSHOT</version>
    </dependency>
  </dependencies>
</project>

重要:

  • 如果在 app 中需要 core,则在 app 模块的 module-info.java 中必须写上 requires core;
  • 只要正确声明了 <dependency><module>,Maven 会自动解析并构建依赖。

Maven 如何处理 module-info.java

  • Maven 会自动与其他类一起编译 module-info.java
  • 如果存在模块化依赖,Maven 会在编译与运行时将它们加入 module-path
  • 如果使用过旧的插件或低于 9 的 Java 版本,模块系统将无法工作。

通过 Maven 运行模块化应用

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.10.1</version>
      <configuration>
        <release>21</release> <!-- 或你的 Java 版本 -->
      </configuration>
    </plugin>
    <plugin>
      <groupId>org.codehaus.mojo</groupId>
      <artifactId>exec-maven-plugin</artifactId>
      <version>3.1.0</version>
      <configuration>
        <mainClass>com.example.app.Main</mainClass>
        <commandlineArgs></commandlineArgs>
      </configuration>
    </plugin>
  </plugins>
</build>
mvn clean install
mvn -pl app exec:java

使用 Maven 与模块时的常见错误

错误:“module not found”。未正确指定 module-path,或没有为依赖的构件声明 <dependency>

模块名重复。项目中不应存在两个同名模块(例如两个 module core)。

缺少 export/requires。若忘记导出包(exports)或声明依赖(requires),会出现编译错误。

混用 classpath 与 module-path。不要使用不支持模块的过时插件/配置,否则会导致不可预测的错误。

3. Gradle:构建多模块项目

Gradle 项目结构

myproject/
  settings.gradle
  build.gradle
  core/
    build.gradle
    src/main/java/module-info.java
    src/main/java/com/example/core/...
  app/
    build.gradle
    src/main/java/module-info.java
    src/main/java/com/example/app/...

settings.gradle

rootProject.name = 'myproject'
include 'core', 'app'

build.gradle(根项目)

subprojects {
    apply plugin: 'java'

    java {
        toolchain {
            languageVersion = JavaLanguageVersion.of(21) // 或你的 Java 版本
        }
    }

    repositories {
        mavenCentral()
    }
}

core 的 build.gradle

plugins {
    id 'java'
}

group = 'com.example'
version = '1.0-SNAPSHOT'

app 的 build.gradle

plugins {
    id 'java'
    id 'application'
}

group = 'com.example'
version = '1.0-SNAPSHOT'

dependencies {
    implementation project(':core')
}

application {
    mainModule = 'app' // 你的 Java 模块名
    mainClass = 'com.example.app.Main'
}

构建与运行

./gradlew build
./gradlew :app:run

如果检测到 module-info.java,Gradle 会自动推断 module-path。若仍无法工作,请检查你使用的是较新的 Gradle 及其插件版本。

4. IntelliJ IDEA:创建与配置模块化项目

创建模块化项目

  • File → New → Project → Java → 勾选“Create module-info.java”。
  • 对于多模块项目:File → New → Module —— 向项目中添加新模块。
  • 如果使用 Maven/Gradle —— 直接通过 pom.xmlbuild.gradle 导入项目即可。

IDEA 中的结构

  • 在 Project View 中,每个模块都会显示为一条独立分支。
  • 每个模块都有自己的 module-info.java 和源代码。
  • IDEA 会提示哪些模块导出/需要哪些包(exports/requires)。
  • 如果忘记导出包或声明依赖——IDEA 会高亮错误。

检查 module-path,运行 main 类

  • 在 Run/Debug Configurations 中指定:
    • Main class(例如,com.example.app.Main
    • Module(例如,app
  • IDEA 会自动处理 module-path
  • 如果通过 Maven/Gradle 启动——使用相应的运行配置(例如,app [run])。

导入第三方模块与库

  • 使用外部库时(例如,requires java.sql; 或第三方 JAR)——通过 Maven/Gradle 添加依赖。
  • 如果库不是模块化的(没有 module-info.java),它会自动被放入“unnamed module”(对大多数库来说这没问题)。

5. 实操:创建一个简单的多模块项目

步骤 1. 创建项目结构

myproject/
  core/
    src/main/java/module-info.java
    src/main/java/com/example/core/HelloService.java
  app/
    src/main/java/module-info.java
    src/main/java/com/example/app/Main.java

HelloService.java (core):

package com.example.core;

public class HelloService {
    public String getHello() {
        return "来自 core 模块的问候!";
    }
}

module-info.java (core):

module core {
    exports com.example.core;
}

Main.java (app):

package com.example.app;

import com.example.core.HelloService;

public class Main {
    public static void main(String[] args) {
        HelloService service = new HelloService();
        System.out.println(service.getHello());
    }
}

module-info.java (app):

module app {
    requires core;
}

步骤 2. 通过 Maven 构建并运行

在项目根目录创建 parent pom.xml 和两个子项目(core/pom.xmlapp/pom.xml),如上所示。在 app/pom.xml 中添加对 core 的依赖。

mvn clean install
mvn -pl app exec:java

步骤 3. 通过 Gradle 构建并运行

创建 settings.gradle、根 build.gradle,并为每个模块各建一个。在 app/build.gradle 中声明对 core 的依赖和 mainClass

./gradlew build
./gradlew :app:run

步骤 4. 在 IntelliJ IDEA 中导入并运行

  • 通过 File → Open 打开项目根目录。
  • IDEA 会识别 Maven/Gradle 结构并创建模块。
  • 在每个模块中打开 module-info.java,IDEA 会展示模块之间的关系。
  • 通过上下文菜单运行 Main.java(“Run Main.main()”)——IDEA 会自动设置 module-path

6. 构建模块化项目的常见错误

错误 1:module-path 不正确。如果在运行或编译时出现类似“module not found”或“package is not visible”的信息,很可能是没有正确指定 module-path,或忘记在 pom.xml/build.gradle 中添加依赖。

错误 2:模块名重复。项目中不应出现两个同名模块(例如两个 module core)。这会导致编译错误。

错误 3:未导出的包。如果类位于未通过 exportsmodule-info.java 中导出的包中,则其他模块将无法访问该类,即使它是 public。尝试使用该类时会出现编译错误。

错误 4:缺少 requires 声明。如果模块使用了另一个模块的类,但没有在 module-info.java 中写 requires,编译器会报错。

错误 5:与外部库不兼容。模块与没有 module-info.java 的库(它们会落入“unnamed module”)之间的“不匹配”通常无碍,但在某些配置下可能需要额外设置。

错误 6:从错误的模块启动。IDEAMaven/Gradle 的运行配置中指定了错误的 main 类或模块——应用将无法启动。请检查 MainmainModule/module 以及所用的 profile。

1
调查/小测验
模块和项目结构第 60 级,课程 4
不可用
模块和项目结构
模块和项目结构
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION