マルチモジュールプロジェクトの作成

このガイドでは、Spring Boot を使用してマルチモジュールプロジェクトを作成する方法を示します。プロジェクトには、ライブラリ jar と、ライブラリを使用するメインアプリケーションがあります。また、これを使用して、ライブラリ(つまり、アプリケーションではない jar ファイル)を単独でビルドする方法を確認することもできます。

構築するもの

単純な "Hello, World" メッセージのサービスを公開するライブラリ jar をセットアップし、ライブラリを依存関係として使用する Web アプリケーションにサービスを含めます。

必要なもの

本ガイドの完成までの流れ

ほとんどの Spring 入門ガイドと同様に、最初から始めて各ステップを完了するか、すでに慣れている場合は基本的なセットアップステップをバイパスできます。いずれにしても、最終的に動作するコードになります。

最初から始めるには、ルートプロジェクトを作成するに進みます。

基本スキップするには、次の手順を実行します。

完了したときは、gs-multi-module/complete のコードに対して結果を確認できます。

最初に、基本的なビルドスクリプトを設定します。Spring を使用してアプリをビルドする場合、好きなビルドシステムを使用できますが、Gradle (英語) および Maven (英語) を操作するために必要なコードはここに含まれています。どちらにも詳しくない場合は、Gradle で Java プロジェクトの構築または Maven で Java プロジェクトの構築を参照してください。

ルートプロジェクトを作成する

このガイドでは、2 つのプロジェクトの構築について説明します。1 つは他のプロジェクトへの依存関係です。ルートプロジェクトに 2 つの子プロジェクトを作成する必要があります。ただし、最初に、最上位でビルド構成を作成します。Maven の場合、サブディレクトリをリストする <modules> を持つ 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>

    <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 の使用方法を示します。

ライブラリプロジェクトを作成する

2 つのプロジェクトの 1 つは、他のプロジェクト(アプリケーション)が使用するライブラリとして機能します。

ディレクトリ構造を作成する

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 を親プロジェクトとして使用して構成します。別の方法として、依存関係管理を pom.xml ファイルの <dependencyManagement/> セクションの部品表 (BOM) [Apache] (英語) としてインポートします。

ライブラリプロジェクトのセットアップ

Library プロジェクトの場合、依存関係を追加する必要はありません。基本的な spring-boot-starter 依存関係は、必要なすべてを提供します。

必要な依存関係を含む Maven ビルドファイルを Spring Initializr から直接取得できます。次のリストは、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>

必要な依存関係を含む Gradle ビルドファイルを Spring Initializr から直接取得できます。次のリストは、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 からライブラリプロジェクトを生成した場合、ビルドシステムのラッパースクリプトが含まれます(選択に応じて mvnw または gradlew)。そのスクリプトとそれに関連する構成をルートディレクトリに移動できます。

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

ライブラリはスターターではなく、最も限定された依存関係に依存する方が良いでしょう。私たち自身で使用するために、org.springframework.boot:spring-boot には必要なコードがすべて含まれています。既存のエントリの -starter を削除すると、ライブラリで過度の依存関係が生じなくなります。

ライブラリプロジェクトには、メインメソッドを持つクラスはありません(アプリケーションではないため)。ビルドシステムに、ライブラリプロジェクトの実行可能ファイル jar をビルドしないように指示する必要があります。(デフォルトでは、Spring Initializr は実行可能プロジェクトをビルドします。)

ライブラリプロジェクトの実行可能 jar をビルドしないように Maven に指示するには、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>

ライブラリプロジェクトの実行可能 jar をビルドしないように Gradle に指示するには、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 プラグインを無効にして無効にする必要があります。

また、Spring Boot プラグインを無効にしたため、-parameters オプションを有効にするために JavaCompiler タスクが自動的に構成されなくなりました。これは、パラメーター名を参照する式を使用している場合に重要です。以下により、このオプションが有効になります。

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 は 1 つだけです)。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 の依存関係が必要です。

必要な依存関係を含む Maven ビルドファイルを Spring Initializr から直接取得できます。次のリストは、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>

必要な依存関係を含む Gradle ビルドファイルを Spring Initializr から直接取得できます。次のリストは、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'
}

アプリケーションを書く

アプリケーションのメインクラスは、ライブラリの Service を使用してメッセージを表示する @RestController です。次のリスト(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: クラスパス設定、他の Bean、さまざまなプロパティ設定に基づいて Bean の追加を開始するよう Spring Boot に指示します。例: spring-webmvc がクラスパスにある場合、このアノテーションはアプリケーションに Web アプリケーションとしてフラグを立て、DispatcherServlet のセットアップなどの主要な動作をアクティブにします。

  • @ComponentScan: Spring に、com/example パッケージ内の他のコンポーネント、構成、サービスを探して、コントローラーを検出させるように指示します。

main() メソッドは、Spring Boot の SpringApplication.run() メソッドを使用してアプリケーションを起動します。XML が 1 行もないことに気付きましたか? 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 から基本パッケージのみを継承します。つまり、scanBasePackageClasses または scanBasePackages を指定すると、明示的に構成されたパッケージスキャンで @EntityScan および @EnableJpaRepositories も明示的に使用する必要がある場合があります。

application.properties ファイルを作成する

application.properties のライブラリにあるサービスにメッセージを提供する必要があります。ソースフォルダーに src/main/resources/application.properties という名前のファイルを作成する必要があります。次のリストは、機能するファイルを示しています。

service.message=Hello, World

アプリケーションをテストする

アプリケーションを起動して、エンドツーエンドの結果をテストします。IDE でアプリケーションを起動するか、コマンドラインを使用できます。アプリケーションが実行されたら、ブラウザーの http://localhost:8080/ でクライアントアプリケーションにアクセスします。そこに、Hello, World がレスポンスに反映されているはずです。

Gradle を使用する場合、次のコマンド(実際には 2 つのコマンドが順番に実行されます)は、最初にライブラリをビルドしてからアプリケーションを実行します。

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

Maven を使用する場合、次のコマンド(実際には 2 つのコマンドが順番に実行されます)は、最初にライブラリをビルドしてからアプリケーションを実行します。

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

要約

おめでとう! Spring Boot を使用して再利用可能なライブラリを作成し、そのライブラリを使用してアプリケーションを構築しました。

関連事項

次のガイドも役立ちます。

新しいガイドを作成したり、既存のガイドに貢献したいですか? 投稿ガイドラインを参照してください [GitHub] (英語)

すべてのガイドは、コード用の ASLv2 ライセンス、およびドキュメント用の Attribution、NoDerivatives creative commons ライセンス (英語) でリリースされています。

コードを入手する