建立多模組專案

本指南將示範如何使用 Spring Boot 建立多模組專案。此專案將包含一個程式庫 jar 檔和一個使用該程式庫的主應用程式。您也可以使用它來了解如何單獨建置程式庫(即,一個不是應用程式的 jar 檔)。

您將建置的內容

您將設定一個程式庫 jar 檔,公開一個用於簡單 “Hello, World” 訊息的服務,然後將該服務包含在一個 Web 應用程式中,該應用程式將該程式庫作為依賴項使用。

您需要的東西

如何完成本指南

如同大多數 Spring 入門指南,您可以從頭開始並完成每個步驟,或者您可以跳過您已熟悉的基礎設定步驟。無論哪種方式,您最終都會得到可運作的程式碼。

若要從頭開始,請繼續前往建立根專案

若要跳過基礎步驟,請執行以下操作

當您完成時,您可以將您的結果與 gs-multi-module/complete 中的程式碼進行比較。

首先,您需要設定一個基本的建置腳本。當使用 Spring 建置應用程式時,您可以使用任何您喜歡的建置系統,但此處包含了使用 GradleMaven 所需的程式碼。如果您不熟悉其中任何一個,請參閱使用 Gradle 建置 Java 專案使用 Maven 建置 Java 專案

建立根專案

本指南將逐步引導您建置兩個專案,其中一個專案是另一個專案的依賴項。因此,您需要在根專案下建立兩個子專案。但首先,請在頂層建立建置配置。對於 Maven,您會需要一個 pom.xml,其中包含列出子目錄的 <modules>

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springframework</groupId>
    <artifactId>gs-multi-module</artifactId>
    <version>0.1.0</version>
    <packaging>pom</packaging>

    <modules>
        <module>library</module>
        <module>application</module>
    </modules>

</project>

對於 Gradle,您會需要一個 settings.gradle,其中包含相同的目錄

rootProject.name = 'gs-multi-module'

include 'library'
include 'application'

並且(可選地)您可以包含一個空的 build.gradle(以幫助 IDE 識別根目錄)。

建立目錄結構

在您想要作為根目錄的目錄中,建立以下子目錄結構(例如,在 *nix 系統上使用 mkdir library application

└── library
└── application

在專案的根目錄中,您需要設定一個建置系統,本指南將示範如何使用 Maven 或 Gradle。

建立程式庫專案

兩個專案中的其中一個將作為程式庫,另一個專案(應用程式)將使用它。

建立目錄結構

library 目錄中,建立以下子目錄結構(例如,透過在 *nix 系統上使用 mkdir -p src/main/java/com/example/multimodule/service

└── src
    └── main
        └── java
            └── com
                └── example
                    └── multimodule
                        └── service

現在您需要配置建置工具(Maven 或 Gradle)。在這兩種情況下,請注意 Spring Boot 外掛程式完全沒有在程式庫專案中使用。此外掛程式的主要功能是建立可執行的 “über-jar”,這既不是我們程式庫需要的,也不是我們想要的。

雖然 Spring Boot Maven 外掛程式沒有被使用,但您確實想要利用 Spring Boot 依賴項管理,因此透過使用 Spring Boot 的 spring-boot-starter-parent 作為父專案來配置它。另一種替代方案是將依賴項管理作為物料清單 (BOM) 導入到 pom.xml 檔案的 <dependencyManagement/> 區段中。

設定程式庫專案

對於程式庫專案,您不需要新增依賴項。基本的 spring-boot-starter 依賴項提供了您所需的一切。

您可以直接從 Spring Initializr 取得具有必要依賴項的 Maven 建置檔案。以下列表顯示了當您選擇 Maven 時建立的 pom.xml 檔案

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>library</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>library</name>
	<description>Demo project for Spring Boot</description>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

您可以直接從 Spring Initializr 取得具有必要依賴項的 Gradle 建置檔案。以下列表顯示了當您選擇 Gradle 時建立的 build.gradle 檔案

plugins {
	id 'org.springframework.boot' version '3.3.0'
	id 'io.spring.dependency-management' version '1.1.5'
	id 'java'
}

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

java {
  sourceCompatibility = '17'
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

調整程式庫專案

如果您從 start.spring.io 產生程式庫專案,它將包含建置系統的包裝腳本(mvnwgradlew,取決於您所做的選擇)。您可以將該腳本及其相關配置向上移動到根目錄

$ mv mvnw* .mvn ..
$ mv gradlew* gradle ..

程式庫最好依賴最窄化的依賴項,而不是 starter。對於我們自己的用途,org.springframework.boot:spring-boot 具有我們需要的所有程式碼。移除現有條目的 -starter 可確保程式庫不會引入過多的依賴項。

程式庫專案沒有帶有 main 方法的類別(因為它不是應用程式)。因此,您必須告訴建置系統不要嘗試為程式庫專案建置可執行的 jar 檔。(預設情況下,Spring Initializr 建置可執行專案。)

若要告訴 Maven 不要為程式庫專案建置可執行的 jar 檔,您必須從 Spring Initializr 建立的 pom.xml 中移除以下區塊

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

以下列表顯示了程式庫專案的最終 pom.xml 檔案

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.2.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>library</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>library</name>
	<description>Demo project for Spring Boot</description>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

</project>

若要告訴 Gradle 不要為程式庫專案建置可執行的 jar 檔,您必須將以下區塊新增到 Spring Initializr 建立的 build.gradle

plugins {
  id 'org.springframework.boot' version '3.2.2' apply false
  id 'io.spring.dependency-management' version '1.1.4'
  // ... other plugins
}

dependencyManagement {
  imports {
    mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
  }
}

bootJar 任務嘗試建立可執行的 jar 檔,這需要一個 main() 方法。因此,您需要透過停用 Spring Boot 外掛程式來停用它,同時保留它以用於其依賴項管理功能。

此外,現在我們已停用 Spring Boot 外掛程式,它不再自動配置 JavaCompiler 任務以啟用 -parameters 選項。如果您使用引用參數名稱的表達式,這點很重要。以下程式碼啟用此選項

tasks.withType(JavaCompile).configureEach {
  options.compilerArgs.add("-parameters")
}

以下列表顯示了程式庫專案的最終 build.gradle 檔案

plugins {
	id 'org.springframework.boot' version '3.3.0' apply false
	id 'io.spring.dependency-management' version '1.1.5'
	id 'java'
}

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

java {
  sourceCompatibility = '17'
}

repositories {
	mavenCentral()
}

dependencyManagement {
	imports {
		mavenBom org.springframework.boot.gradle.plugin.SpringBootPlugin.BOM_COORDINATES
	}
}

dependencies {
	implementation 'org.springframework.boot:spring-boot'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.withType(JavaCompile).configureEach {
	options.compilerArgs.add("-parameters")
}

建立服務元件

程式庫將提供一個 MyService 類別,應用程式可以使用它。以下列表(來自 library/src/main/java/com/example/multimodule/service/MyService.java)顯示了 MyService 類別

package com.example.multimodule.service;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Service;

@Service
@EnableConfigurationProperties(ServiceProperties.class)
public class MyService {

  private final ServiceProperties serviceProperties;

  public MyService(ServiceProperties serviceProperties) {
    this.serviceProperties = serviceProperties;
  }

  public String message() {
    return this.serviceProperties.getMessage();
  }
}

為了使其能在標準 Spring Boot 慣用語中配置(使用 application.properties),您還可以新增一個 @ConfigurationProperties 類別。ServiceProperties 類別(來自 library/src/main/java/com/example/multimodule/service/ServiceProperties.java)滿足了這個需求

package com.example.multimodule.service;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("service")
public class ServiceProperties {

  /**
   * A message for the service.
   */
  private String message;

  public String getMessage() {
    return message;
  }

  public void setMessage(String message) {
    this.message = message;
  }
}

您不必這樣做。程式庫可能僅僅提供純 Java API,而不提供 Spring 功能。在這種情況下,使用程式庫的應用程式將需要自行提供配置。

測試服務元件

您會想要為您的程式庫元件編寫單元測試。如果您提供可重複使用的 Spring 配置作為程式庫的一部分,您可能還想編寫整合測試,以確保配置正常運作。若要執行此操作,您可以使用 JUnit 和 @SpringBootTest 註解。以下列表(來自 library/src/test/java/com/example/multimodule/service/MyServiceTest.java)顯示了如何執行此操作

package com.example.multimodule.service;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest("service.message=Hello")
public class MyServiceTest {

  @Autowired
  private MyService myService;

  @Test
  public void contextLoads() {
    assertThat(myService.message()).isNotNull();
  }

  @SpringBootApplication
  static class TestConfiguration {
  }

}
在前面的列表中,我們透過使用 @SpringBootTest 註解的預設屬性,為測試配置了 service.message。我們建議將 application.properties 放入程式庫中,因為在執行階段,程式庫與使用該程式庫的應用程式之間可能會發生衝突(永遠只從類別路徑載入一個 application.properties)。您可以application.properties 放入測試類別路徑中,但不將其包含在 jar 檔中(例如,將其放置在 src/test/resources 中)。

建立應用程式專案

應用程式專案使用程式庫專案,後者提供其他專案可以使用的服務。

建立目錄結構

application 目錄中,建立以下子目錄結構(例如,在 *nix 系統上使用 mkdir -p src/main/java/com/example/multimodule/application

└── src
    └── main
        └── java
            └── com
                └── example
                    └── multimodule
                        └── application

除非您想透過應用程式中的 @ComponentScan 包含程式庫中的所有 Spring 元件,否則請勿使用與程式庫相同的套件(或程式庫套件的父套件)。

設定應用程式專案

對於應用程式專案,您需要 Spring Web 和 Spring Boot Actuator 依賴項。

您可以直接從 Spring Initializr 取得具有必要依賴項的 Maven 建置檔案。以下列表顯示了當您選擇 Maven 時建立的 pom.xml 檔案

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>application</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>application</name>
	<description>Demo project for Spring Boot</description>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

您可以直接從 Spring Initializr 取得具有必要依賴項的 Gradle 建置檔案。以下列表顯示了當您選擇 Gradle 時建立的 build.gradle 檔案

plugins {
	id 'org.springframework.boot' version '3.3.0'
	id 'io.spring.dependency-management' version '1.1.5'
	id 'java'
}

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

java {
	sourceCompatibility = '17'
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-actuator'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

您可以刪除 mvnw 和/或 gradlew 包裝器及其相關的配置文件

$ rm -rf mvnw* .mvn
$ rm -rf gradlew* gradle

新增程式庫依賴項

應用程式專案需要依賴程式庫專案。您需要相應地修改您的應用程式建置檔案。

對於 Maven,新增以下依賴項

<dependency>
  <groupId>com.example</groupId>
  <artifactId>library</artifactId>
  <version>${project.version}</version>
</dependency>

以下列表顯示了完成的 pom.xml 檔案

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.0</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>application</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>application</name>
	<description>Demo project for Spring Boot</description>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>com.example</groupId>
			<artifactId>library</artifactId>
			<version>${project.version}</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

對於 Gradle,新增以下依賴項

implementation project(':library')

以下列表顯示了完成的 build.gradle 檔案

plugins {
	id 'org.springframework.boot' version '3.3.0'
	id 'io.spring.dependency-management' version '1.1.5'
	id 'java'
}

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

java {
	sourceCompatibility = '17'
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-actuator'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation project(':library')
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

編寫應用程式

應用程式中的主類別可以是 @RestController,它使用程式庫中的 Service 來呈現訊息。以下列表(來自 application/src/main/java/com/example/multimodule/application/DemoApplication.java)顯示了這樣一個類別

package com.example.multimodule.application;

import com.example.multimodule.service.MyService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication(scanBasePackages = "com.example.multimodule")
@RestController
public class DemoApplication {

  private final MyService myService;

  public DemoApplication(MyService myService) {
    this.myService = myService;
  }

  @GetMapping("/")
  public String home() {
    return myService.message();
  }

  public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args);
  }
}

@SpringBootApplication 是一個方便的註解,它新增了以下所有內容

  • @Configuration:將類別標記為應用程式情境的 Bean 定義來源。

  • @EnableAutoConfiguration:告訴 Spring Boot 開始根據類別路徑設定、其他 Bean 和各種屬性設定來新增 Bean。例如,如果 spring-webmvc 在類別路徑上,則此註解會將應用程式標記為 Web 應用程式並啟動關鍵行為,例如設定 DispatcherServlet

  • @ComponentScan:告訴 Spring 在 com/example 套件中尋找其他元件、配置和服務,使其能夠找到控制器。

main() 方法使用 Spring Boot 的 SpringApplication.run() 方法來啟動應用程式。您是否注意到沒有單行 XML?也沒有 web.xml 檔案。此 Web 應用程式是 100% 純 Java,您不必處理配置任何管線或基礎架構。

因為 DemoApplication 位於與 MyService (com.example.multimodule.service) 不同的套件 (com.example.multimodule.application) 中,所以 @SpringBootApplication 無法自動偵測到它。有不同的方法可以讓 `MyService` 被選取

  • 使用 @Import(MyService.class) 直接匯入它。

  • 透過使用 @SpringBootApplication(scanBasePackageClasses={…​}) 從其套件中獲取所有內容。

  • 依名稱指定父套件:com.example.multimodule。(本指南使用此方法)

如果您的應用程式也使用 JPA 或 Spring Data,則 @EntityScan@EnableJpaRepositories(以及相關的)註解僅從 @SpringBootApplication 繼承其基本套件,除非明確指定。也就是說,一旦您指定 scanBasePackageClassesscanBasePackages,您可能還必須明確使用 @EntityScan@EnableJpaRepositories 及其明確配置的套件掃描。

建立 application.properties 檔案

您需要在 application.properties 中為程式庫中的服務提供訊息。在原始碼資料夾中,您需要建立一個名為 src/main/resources/application.properties 的檔案。以下列表顯示了一個可運作的檔案

service.message=Hello, World

測試應用程式

透過啟動應用程式來測試端對端結果。您可以在 IDE 中啟動應用程式,或使用命令列。應用程式執行後,在瀏覽器中造訪客戶端應用程式,網址為 https://127.0.0.1:8080/。在那裡,您應該會在回應中看到 Hello, World

如果您使用 Gradle,以下命令(實際上是依序執行的兩個命令)將首先建置程式庫,然後執行應用程式

$ ./gradlew build && ./gradlew :application:bootRun

如果您使用 Maven,以下命令(實際上是依序執行的兩個命令)將首先建置程式庫,然後執行應用程式

$ ./mvnw install && ./mvnw spring-boot:run -pl application

摘要

恭喜!您已使用 Spring Boot 建立了一個可重複使用的程式庫,然後使用該程式庫來建置應用程式。

另請參閱

以下指南可能也很有幫助

想要編寫新的指南或貢獻現有的指南嗎?請查看我們的貢獻指南

所有指南均以 ASLv2 授權發布程式碼,並以 姓名標示-禁止改作創用 CC 授權 發布寫作內容。

取得程式碼