2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Spring Boot 4】ゼロから作るタスク管理API ①環境構築〜Hello World、テストまで

2
Last updated at Posted at 2026-10-08

note. (40).png

はじめに

Javaの実務経験はありますが、Spring Bootはまったくの初学者です。
このシリーズでは、Spring Bootをゼロから学びながら「JWT認証付きのタスク管理REST API」を完成させるまでの過程を、全8本の記事にまとめていきます。

# テーマ
① 環境構築編:プロジェクト作成、Hello World(この記事)
② 核となる概念編:DI/IoCコンテナ、アノテーション駆動
③ 最初のREST API編:タスクの基本CRUD(メモリ上)
④ DB連携編:Spring Data JPA、H2→PostgreSQL
⑤ 設定・プロファイル編:application.yamlで環境分離
⑥ テスト編:JUnit + MockMvc
⑦ 認証・認可編:ユーザー登録・ログイン・JWT
⑧ 総仕上げ編:ページネーション・絞り込み、Docker化

単に「動くものを作る」だけでなく、「なぜこう書くのか」を自分の言葉で説明できるようになることを目標にしています。

進め方:Claude Codeに「手順書」を作ってもらう

学習のお供として、AIコーディングツールの Claude Code を使っています。ただし、コードはClaude Codeに書かせず、自分の手で書くというルールにしました。
Claude Codeには「実行するコマンド」「作成するファイルのパスと中身」「なぜそう書くのか」をまとめた手順書を作ってもらい、それを見ながら自分で実装して、結果を報告する……という流れで進めています。

全部AIに書かせると早いのですが、それでは自分の理解が追いつかないためです。

この記事のゴール

  • Spring Bootのプロジェクトを作成する
  • GET /hello にアクセスすると Hello, Spring Boot! が返ってくる
  • そのエンドポイントを自動テストで確認できる

環境

項目 バージョン
OS Windows 11
Spring Boot 4.1.1
Java 21(Eclipse Temurin 21.0.5)
ビルドツール Maven(Maven Wrapper経由)
ターミナル PowerShell

3.5系ではなく4.1.1を選んだ理由

最初は「長く使われそうな安定版がいいだろう」と考えて、Spring Boot 3.5系を使うつもりでした。
ところが調べてみると、3.5系はOSS版のサポートがすでに終了していました(2026年6月30日)。実際に Spring Initializr を開いても、選択肢には4.0系以降しか出てきません。

これから学ぶのに、サポートが終わったバージョンを選ぶ理由はありません。そこで、執筆時点(2026年10月)で最新の安定版である 4.1.1 を使うことにしました。

ネット上の記事は3系向けのものがまだ多いので、4系では書き方が変わっている部分に注意が必要です。この記事でも、実際に引っかかりそうなポイントを2つ紹介します。

手順1:Java 21 を使えるようにする

Spring Boot 4を動かすには、Java 17以上が必要です。今回は、長期サポート版(LTS)の Java 21 を使います。

さっそく java -version を確認してみたところ……

java version "1.8.0_391"

Java 8でした。 環境変数 Path 上にある、PCに以前から入っていた Java 8 が使われていました。さらに環境変数 JAVA_HOME も、Eclipseの日本語化パッケージである Pleiades に入っている Java 8 を指していました。

幸い、Pleiadesには Java 21 も同梱されていたので(C:\pleiades\2024-12\java\21)、新しくインストールする必要はありませんでした。PowerShellで次のように切り替えます。

$env:JAVA_HOME = "C:\pleiades\2024-12\java\21"
$env:Path = "$env:JAVA_HOME\bin;$env:Path"
java -version

01_java-version-21.png

JAVA_HOME は、Mavenなどのツールが「どのJDKを使うか」を判断するための環境変数です。一方、ターミナルで java と打ったときに実行されるのは Path 上で最初に見つかった java です。そのため、2行目で Java 21 の bin フォルダを Path の先頭に追加しています。

ここでは、システム全体の環境変数は変更せず、このターミナルの中だけで切り替えています。 システム全体を変えてしまうと、Java 8を前提にしている他のツールや作業に影響が出るおそれがあるからです。
そのかわり、ターミナルを閉じると設定が消えるので、作業のたびにこの3行を実行する必要があります。

手順2:Spring Initializr でプロジェクトを作る

Spring Initializr は、Spring Bootプロジェクトのひな形をブラウザ上で作れる公式のサービスです。次のように設定して「GENERATE」を押すと、zipファイルがダウンロードされます。

02_spring-initializr.png

項目 設定値 補足
Project Maven
Language Java
Spring Boot 4.1.1 SNAPSHOT や M(マイルストーン)が付いたものは開発中の版なので選ばない
Group com.example
Artifact task-management-api
Package name com.example.taskmanagementapi ハイフンは自動で取り除かれる
Packaging Jar
Configuration YAML 理由は手順4で説明します
Java 21
Dependencies Spring Web

依存関係(Dependencies)は、Spring Web だけにしました。データベースや入力チェックなどの機能は、必要になった回で理由と一緒に追加していく方針です。

ダウンロードしたzipを展開すると、task-management-api フォルダができます。

手順3:生成されたファイルを見る

生成されたプロジェクトの主な構成は次のとおりです。

task-management-api/
├── mvnw / mvnw.cmd          ← Maven Wrapper
├── .mvn/
├── pom.xml                  ← 依存関係などの設定
└── src/
    ├── main/
    │   ├── java/com/example/taskmanagementapi/
    │   │   └── TaskManagementApiApplication.java   ← 起動クラス
    │   └── resources/
    │       └── application.yaml                    ← 設定ファイル
    └── test/
        └── java/com/example/taskmanagementapi/
            └── TaskManagementApiApplicationTests.java

Maven Wrapper(mvnw)とは

私のPCにはMaven本体がインストールされていませんでした。それでも問題なく進められたのは、Maven Wrapper のおかげです。

mvnw.cmd(Windows用)を実行すると、プロジェクトで指定されたバージョンのMavenが自動でダウンロードされて使われます。Mavenを事前にインストールする必要がなく、人によってMavenのバージョンが違う、という問題も起きにくくなります。

pom.xml:4系での名前の変更に注意

pom.xml を開くと、依存関係が次のようになっていました。

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

ここが 4系で注意したいポイントその1 です。
3系までの記事では、Web機能の依存関係は spring-boot-starter-web と書かれていることがほとんどです。4系では spring-boot-starter-webmvc という名前になりました。旧名の spring-boot-starter-web も非推奨(deprecated)として残っていますが、これから作るプロジェクトでは新しい名前を使います。
Initializrの画面では、以前と同じ「Spring Web」という表示なので、気づきにくいところです。

テスト用にも、spring-boot-starter-webmvc-test という専用の依存関係が追加されています。

手順4:application.yaml を確認する

Spring Bootの設定ファイルには、properties形式とYAML形式の2種類があります。今回はInitializrで YAML を選んだので、application.yaml が最初から作られています。

spring:
  application:
    name: task-management-api

同じ内容をproperties形式で書くと、次の1行になります。

spring.application.name=task-management-api

YAMLは、ドットで区切っていた部分をインデントで表します。今はまだ設定が1つだけなのでありがたみがありませんが、④DB連携編や⑤設定・プロファイル編で設定が増えたときに、ぐっと読みやすくなるはずです。

YAMLで気をつけたいのは次の3点です。

  • インデントにタブは使えません。 スペースで揃えます(2つずつが一般的です)
  • : の後ろには半角スペースが必要です。 name:task-management-api では正しく読み込まれません
  • properties形式とYAML形式のファイルを両方置かないようにします。 同じ場所に両方あると properties 側が優先され、どちらの設定が効いているのか分かりにくくなります

手順5:HelloController を作る

いよいよコードを書きます。起動クラスと同じ階層に hello パッケージ(フォルダ)を作り、その中に HelloController.java を作成します。

src/main/java/com/example/taskmanagementapi/
├── TaskManagementApiApplication.java
└── hello/
    └── HelloController.java   ← 新規作成
HelloController.java
package com.example.taskmanagementapi.hello;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello, Spring Boot!";
    }
}

たった数行ですが、ポイントがいくつかあります。

  • @RestController
    このクラスが「HTTPリクエストを受け付けて、結果をそのまま返すクラス」であることを示します。@Controller と @ResponseBody を合わせたもので、メソッドの戻り値がレスポンスの本文そのものになります。
  • @GetMapping("/hello")
    GET /hello へのリクエストが来たら、このメソッドを呼び出す、という対応付けです。
  • new していないのに動く理由
    このクラスは、どこからも new HelloController() されていません。それでも動くのは、起動クラスに付いている @SpringBootApplication が、自分と同じパッケージとその配下を自動で探して、@RestController などが付いたクラスを登録してくれるからです(コンポーネントスキャン)。
    逆に、起動クラスのパッケージの外(たとえば com.example.hello)に置くと見つけてもらえず、アクセスしても404になります。

この「Spring が勝手に用意してくれる」仕組みは、次回の②DI/IoC編で詳しく掘り下げる予定です。

手順6:起動して確認する

zipを展開してできた task-management-api フォルダに移動して、アプリケーションを起動します。

cd task-management-api
.\mvnw.cmd spring-boot:run

初回は、Mavenや各種ライブラリのダウンロードがあるため、少し時間がかかります。

04_spring-boot-run-log-crop.png

ログの中で、特に見ておきたいのは次の行です。

ログ 意味
Starting ... using Java 21.0.5 Java 21で動いている
Starting Servlet engine: [Apache Tomcat/11.0.24] 組み込みのWebサーバー(Tomcat)が起動している
Tomcat started on port 8080 (http) 8080番ポートでリクエストを受け付けられる状態になった
Started TaskManagementApiApplication in 1.597 seconds 起動完了

Spring Bootでは、Webサーバーの Tomcat がアプリケーションに組み込まれています。Tomcatを別途インストールして、そこにアプリを配置する……という作業が要らないのは、とても楽だと感じました。

起動したら、ブラウザで http://localhost:8080/hello を開きます。

05_hello-browser.png

動きました! 🎉

PowerShellで確認する場合は、curl ではなく curl.exe と書きます。Windowsに標準で入っている Windows PowerShell 5.1 では、curl が別のコマンド(Invoke-WebRequest)の別名になっているためです(PowerShell 7 以降では、この別名はなくなっています)。

curl.exe http://localhost:8080/hello
# → Hello, Spring Boot!

ちょっとした発見:DispatcherServlet は最初のリクエストで動き出す

起動ログの最後の3行(Initializing Spring DispatcherServlet 'dispatcherServlet' など)は、起動完了から約50秒後の時刻になっていました。これは、ブラウザで /hello に初めてアクセスしたタイミングです。

DispatcherServlet は、届いたリクエストを HelloController などの適切なクラスに振り分ける役割を持っています。起動時ではなく、最初のリクエストが来たときに初期化されることが、ログの時刻から分かりました。

停止するとき

起動したターミナルで Ctrl + C を押します。Windowsでは「バッチ ジョブを終了しますか (Y/N)?」と聞かれるので、y を入力します。
なお、停止後にも BUILD SUCCESS と表示されますが、これは「Mavenのコマンドが正常に終わった」という意味で、テストが成功したわけではありません。

手順7:テストを書く

「テストは後回しにしない」と決めているので、Hello Worldの段階からテストを書きます。

もともとあるテスト

実は、Initializrがテストを1本作ってくれています。

TaskManagementApiApplicationTests.java
@SpringBootTest
class TaskManagementApiApplicationTests {

	@Test
	void contextLoads() {
	}

}

中身は空ですが、@SpringBootTest はアプリケーション全体を本番と同じように組み立てます。そのため、設定ミスなどでSpringの起動に失敗すると、このテストも失敗します。 最低限の安全網として役立つテストです。

HelloController のテストを追加する

テスト側にも同じ hello パッケージを作り、HelloControllerTest.java を作成します。

HelloControllerTest.java
package com.example.taskmanagementapi.hello;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest(HelloController.class)
class HelloControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    void helloReturnsGreeting() throws Exception {
        mockMvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string("Hello, Spring Boot!"));
    }
}
  • @WebMvcTest(HelloController.class)
    Web(Controller)まわりだけを起動する、軽いテストです。アプリケーション全体を組み立てる @SpringBootTest に比べて、速く動きます。
  • MockMvc
    実際にサーバーを立てずに、疑似的なHTTPリクエストを送れる道具です。
  • .andExpect(...)
    ステータスコードが200(OK)であること、レスポンスの本文が Hello, Spring Boot! と完全に一致することを確認しています。

そして、ここが 4系で注意したいポイントその2 です。
@WebMvcTest のパッケージが、3系から変わっています。

バージョン import
3系 org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
4系 org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest

ネット上の記事の多くは3系のimportのままなので、そのままコピーするとコンパイルエラーになります。

テストを実行する

.\mvnw.cmd test

07_mvnw-test-success-crop.png

Tests run: 2 は、自動生成された contextLoads と、今回追加した helloReturnsGreeting の2本です。

なお、実行中に次のような WARNING が表示されますが、テストの結果には影響ありません。

Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. ...
WARNING: A Java agent has been loaded dynamically (...byte-buddy-agent-1.18.11.jar)
...
WARNING: Dynamic loading of agents will be disallowed by default in a future release

テスト用のライブラリである Mockito が、実行中に Java のエージェント(実行時にクラスを書き換える仕組み)を読み込んでいることへの警告です。Java 21 から、このような動的な読み込みに対して警告が出るようになりました(JEP 451)。対処方法は⑥テスト編で扱う予定です。

わざとテストを失敗させてみる

テストが本当に間違いを見つけてくれるのか、確かめてみました。HelloController の戻り値を、わざと書き換えます。

return "Hello";   // わざと間違える

テストのほうは「Hello, Spring Boot! が返るはず」のまま、mvnw test を実行すると……

09_mvnw-test-failure-summary.png

[ERROR]   HelloControllerTest.helloReturnsGreeting:22 Response content expected:<Hello, Spring Boot!> but was:<Hello>
[ERROR] Tests run: 2, Failures: 1, Errors: 0, Skipped: 0

しっかり失敗してくれました。このログから、次のことが読み取れます。

  • HelloControllerTest.helloReturnsGreeting:22:どのテストの何行目で失敗したか(22行目は content().string(...) の行)
  • expected:<Hello, Spring Boot!> but was:<Hello>:期待していた値と、実際に返ってきた値
  • Tests run: 2, Failures: 1:2本のうち、失敗したのは1本だけ。アプリが起動できるかを確かめる contextLoads は成功したまま

つまり、「アプリは起動するけれど、返す内容が間違っている」という状態を、HelloControllerTest が見つけてくれたわけです。2本のテストが、それぞれ別のものを守っていることがよく分かりました。

さらに、失敗したときにはリクエストとレスポンスの詳細も自動で表示されていました。

08_mvnw-test-failure-mockmvc-crop.png

Status = 200 なのに Body = Hello になっていることから、「ステータスは正常だが、本文が違う」と一目で分かります。原因を探すときに、とても役立つ情報です。

最後に、戻り値を Hello, Spring Boot! に戻して、もう一度 mvnw test を実行すると、再び BUILD SUCCESS になりました。

つまずいたポイントまとめ

つまずき 原因 対処
java -version が 1.8 だった 以前から入っていた Java 8 が使われていた ターミナル単位で JAVA_HOME を Java 21 に切り替え
3.5系が Initializr にない OSS版のサポートが終了していた 最新の安定版 4.1.1 を採用
依存関係の名前がネット上の記事と違う 4系で spring-boot-starter-webmvc に変更 Initializr が生成した pom.xml をそのまま使う
@WebMvcTest の import 4系でパッケージが変更 org.springframework.boot.webmvc.test.autoconfigure を使う
PowerShell の curl Windows PowerShell 5.1 では Invoke-WebRequest の別名 curl.exe と書く
停止時の BUILD SUCCESS Maven コマンドが正常に終わっただけ テストの成功と取り違えない

まとめ

今回は、Spring Bootのプロジェクトを作成し、/hello が動いて、テストが通るところまで進めました。

  • Spring Boot 4.1.1 + Java 21 でプロジェクトを作成した
  • @RestController と @GetMapping だけで、APIのエンドポイントが作れた
  • @WebMvcTest と MockMvc で、サーバーを立てずにテストできた
  • わざと失敗させることで、テストが何を守っているのかを実感できた

一番印象に残ったのは、ネット上の情報がすでに古くなっていることが多いという点です。3.5系のサポート終了、依存関係の名前の変更、@WebMvcTest のパッケージ変更と、今回だけでも3つありました。「この記事はどのバージョン向けか」を意識しながら調べる習慣が大事だと感じました。

次回予告

次回は②核となる概念編です。今回、HelloController は一度も new していないのに動きました。この「なぜ動くのか」を、DI(依存性の注入)とIoCコンテナという考え方から掘り下げていきます。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?