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 必須能看到你的應用程式所需的所有模組(以及它們的相依)。

範例:手動編譯模組

假設你有兩個模組:core 與 app,其中 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/Gradle 或 IDE。

2. Maven:多模組專案的建置

Maven 中的多模組專案結構

Maven 早已支援包含多個模組(submodules)的結構。隨著 Java 模組系統的出現,這些 Maven 的「模組」經常會與 Java 模組對應,但不一定總是如此:Maven 模組只是子專案,而 Java 模組則是寫在 module-info.java 中的定義。通常一個 Maven 模組 = 一個 Java 模組。

典型結構:

myproject/
  pom.xml                # Parent 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。
  • 如果你使用舊版外掛或 Java 版本低於 9——模組系統將無法運作。

透過 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.xml 或 build.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.xml、app/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:未匯出的套件。 若類別位於未透過 exports 於 module-info.java 匯出的套件中,即使是 public,對其他模組仍不可見;嘗試使用時會發生編譯錯誤。

錯誤 4:未宣告的 requires。 若模組使用了其他模組的類別,卻沒有在 module-info.java 中加入 requires,編譯器會回報錯誤。

錯誤 5:與外部函式庫的不相容。 模組與沒有 module-info.java 的函式庫(它們會被放入「unnamed module」)通常不會互相干擾,但在某些設定下可能需要額外調整。

錯誤 6:從錯誤的模組啟動。 在 IDEA 或 Maven/Gradle 的執行組態中指定了錯誤的 main 類別或模組——應用程式將無法啟動。請檢查 Main、mainModule/module 與使用中的設定檔。

1
任務
JAVA 25 SELF, 等級 60, 課堂 4
上鎖
構建多模組專案的堡壘 🏰
構建多模組專案的堡壘 🏰
1
任務
JAVA 25 SELF, 等級 60, 課堂 4
上鎖
透過 Maven 啟動應用程式的核心 ✨
透過 Maven 啟動應用程式的核心 ✨
1
問卷/小測驗
模組與專案結構,等級 60,課堂 4
未開放
模組與專案結構
模組與專案結構
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION