---
title: "Spring WebFluxとは？MVCとの違い・Mono/Fluxの基本とSpring Boot 4.1での使い方"
url: "https://www.issoh.co.jp/tech/details/3208/"
published: 2024-07-31
updated: 2026-09-29
categories: ["Spring"]
publisher: "株式会社一創"
---

# Spring WebFluxとは？MVCとの違い・Mono/Fluxの基本とSpring Boot 4.1での使い方

Spring WebFluxは、Spring Frameworkに含まれるノンブロッキングなWebフレームワークです。2017年9月のSpring Framework 5.0で加わり、2026年9月時点の最新はSpring Framework 7.0.9（Spring Boot 4.1.1が採用する版）です。少数のイベントループスレッドで多数のリクエストをさばくため、スレッドを止めない前提で書く必要があります。

この記事では、Spring MVCとの違い、Reactorの`Mono`と`Flux`、Spring Boot 4.1.1での最小構成、ブロッキング処理を混ぜたときの挙動を実測値つきで解説します。WebFluxを選ぶべきか迷っている方は、先にまとめと「採用すべき場面と避けるべき場面」の章を読んでください。

## まとめ：Spring WebFluxの要点とMVCとの選び分け

- Spring WebFluxはReactive Streamsに基づくノンブロッキングなWebフレームワークで、既定のサーバーはNetty（Reactor Netty）です。
- Spring MVCは「リクエストごとにスレッドを占有してよい」前提、WebFluxは「スレッドを止めない」前提です。違いはこの前提と、それを受けたスレッドモデルにあります。
- 戻り値は1件なら`Mono`（0〜1件）、複数件やストリームなら`Flux`（0〜N件）を使います。
- JPAやJDBCのようなブロッキングAPIが中心なら、公式リファレンスもSpring MVCを推奨しています。WebFluxからリレーショナルDBへノンブロッキングでアクセスするなら、対応ドライバーのあるR2DBCが候補です。MongoDBなどには別のリアクティブ対応があります。
- Spring Boot 4ではWebClientの自動構成が別スターター（`spring-boot-starter-webclient`）に分かれました。`spring-boot-starter-webflux`だけでは`WebClient.Builder`のBeanが作られません。
- 動いているMVCアプリを移行する必要はありません。まず外部API呼び出しだけWebClientに置き換え、効果を測ってから広げます。Java 21以降では仮想スレッドという選択肢もあります。

## Spring WebFluxの定義と2026年9月時点の構成バージョン

WebFluxは、Spring Frameworkの`spring-webflux`モジュールです。Servlet APIに依存するSpring MVC（`spring-webmvc`）とは別系統の「リアクティブスタック」として、Spring Framework 5.0（GitHubのタグ`v5.0.0.RELEASE`は2017年9月28日）で追加されました。リアクティブライブラリにはProject Reactorを使い、Reactive Streams仕様のバックプレッシャー（受け手が処理できる量だけ要求する仕組み）に対応します。

Spring Boot 4.1.1（2026年8月20日公開）で`spring-boot-starter-webflux`を使った場合、BOM（依存バージョンをまとめて管理する定義）が引き込む主要ライブラリの版は次のとおりです。最新の対応表はSpring Bootリファレンスの[Managed Dependency Coordinates](https://docs.spring.io/spring-boot/appendix/dependency-versions/coordinates.html)で確認できます。

| ライブラリ                            | バージョン        | 役割              |
| -------------------------------- | ------------ | --------------- |
| Spring Framework（spring-webflux） | 7.0.9        | WebFlux本体       |
| Reactor Core                     | 3.8.7        | Mono・Flux       |
| Reactor Netty                    | 1.3.7        | HTTPサーバー／クライアント |
| Netty                            | 4.2.17.Final | 非同期I/O基盤        |
| Jackson                          | 3.1.5        | JSON変換          |

Spring Boot 4.1系のOSSサポートは2027年7月31日までです。4.0系は2026年12月31日で終わり、3.5系は2026年6月30日に終了しました。版ごとのサポート期限は[Spring Bootのバージョン一覧とサポート期限](/tech/details/5172/)で、4系の変更点全体は[Spring Boot 4の変更点と3との違い](/tech/details/10124/)で整理しています。

## Spring MVCとの違い：スレッドモデルとブロッキングの前提

公式リファレンスの「[Concurrency Model](https://docs.spring.io/spring-framework/reference/web/webflux/new-framework.html#webflux-concurrency-model)」は、両者の違いを「ブロッキングとスレッドに関する既定の前提」だと説明しています。Spring MVCはアプリケーションが現在のスレッドをブロックしうると考え、Servletコンテナは大きなスレッドプールでそのブロックを吸収します。WebFluxはアプリケーションがブロックしないと考え、少数の固定スレッド（イベントループ）でリクエストを処理します。

Reactor Nettyのイベントループ数は、既定で「JVMが使えるプロセッサ数（最小4）」です。16論理CPUのマシンでSpring Boot 4.1.1を起動すると、リクエストを処理するスレッドは`reactor-http-nio-1`から`reactor-http-nio-16`の16本でした。Tomcatの既定最大200スレッドとは桁が違います。

| 比較項目         | Spring MVC                 | Spring WebFlux              |
| ------------ | -------------------------- | --------------------------- |
| 基盤API        | Servlet API                | Reactive Streams            |
| 既定サーバー       | Tomcat                     | Netty（Reactor Netty）        |
| 他の対応サーバー     | Jetty                      | Tomcat・Jetty                |
| スレッドの持ち方     | 1リクエスト1スレッド                | 少数のイベントループ                  |
| ブロッキング呼び出し   | 許容                         | イベントループ上では不可                |
| コントローラーの戻り値  | 通常の型・Mono・Fluxなど           | Mono・Flux・通常の型など            |
| 書き方          | アノテーション／関数型                | アノテーション／関数型                 |
| DBアクセス（RDB）  | JDBC・JPA                   | R2DBC                       |
| Boot 4のスターター | spring-boot-starter-webmvc | spring-boot-starter-webflux |

`@RestController`や`@GetMapping`といったアノテーションは両方で使えるので、コードの見た目は似ています。一方、同じ書き方で中にブロッキング処理を入れると、WebFluxでは次の章で示すとおり性能が大きく落ちます。Spring MVC側の構成とSpring Boot 4での依存名の変更は[Spring MVCの解説記事](/tech/details/10533/)にまとめています。

両方のスターターを依存に入れた場合、Spring BootはSpring MVCを自動構成します。MVCアプリにWebClientだけを使う目的でWebFluxを追加する開発者が多いため、とSpring Bootのリファレンスは理由を説明しています。WebFluxで起動させたい場合は`SpringApplication.setWebApplicationType(WebApplicationType.REACTIVE)`で明示します。

## MonoとFluxの基本とReactive Streams

### Mono（0〜1件）とFlux（0〜N件）の使い分け

Reactorのリファレンスは、`Flux<T>`を「0〜N個の要素を流す非同期シーケンス」、`Mono<T>`を「0〜1個の結果」と定義しています。どちらもReactive Streamsの`Publisher`で、正常終了時は`onComplete`、異常終了時は`onError`を通知します。ただし、終了しないストリームや、終了シグナルを通知せず購読がキャンセルされる場合もあります。

- IDで1件取得する、登録結果を返す：`Mono<User>`
- 一覧を返す、Server-Sent Eventsで流し続ける：`Flux<User>`
- 本文を返さない（削除など）：`Mono<Void>`

`Mono`や`Flux`を返すだけでは、処理が購読時まで遅延されるとは限りません。cold publisherは購読（subscribe）を契機に処理を始めますが、hot publisherは購読前から動く場合があります。`Mono.just(load())`では`load()`がその場で実行され、`Mono.fromCallable(() -> load())`なら購読時まで遅延されます。WebFluxでは戻り値のPublisherをフレームワークが購読します。コントローラー内で`block()`を呼んで値を取り出すと、イベントループを止める原因になります。

### Reactorの主要オペレーターと処理の使い分け

MonoやFluxは、オペレーターをつないで処理を宣言します。最初に覚えるのは次の6つで足ります。

| オペレーター        | 用途                      |
| ------------- | ----------------------- |
| map           | 値を同期的に変換                |
| flatMap       | 値から別のMono/Fluxを呼び出して平坦化 |
| filter        | 条件に合う要素だけ残す             |
| zip           | 複数の結果を待ち合わせて結合          |
| onErrorResume | エラー時に代替値へ切り替え           |
| timeout       | 応答の上限時間を設定              |

`map`と`flatMap`の取り違えが最初のつまずきです。変換先が普通の値なら`map`、WebClientやR2DBCの呼び出しのように`Mono`を返す処理なら`flatMap`を使います。`map`で`Mono`を返すと、型が`Mono<Mono<T>>`になり、内側は誰にも購読されません。

### Reactive Streamsとバックプレッシャーの位置づけ

Reactive Streamsは、`Publisher`・`Subscriber`・`Subscription`・`Processor`の4インターフェースで非同期ストリームの約束事を定めた仕様です。受け手が`request(n)`で処理できる件数を伝え、送り手はそれを超えて送りません。WebFluxはこの仕組みをHTTPの読み書きまで通しているので、遅いクライアントに大量データを流してもメモリにため込みにくくなります。流量制御の方式の比較は[バックプレッシャーの仕組みと実装方式の選び分け](/tech/details/16432/)で扱っています。

## Spring Boot 4.1で最小のWebFluxアプリを作る手順

### 依存関係とspring-boot-starter-webfluxの中身

Spring Initializrで「Spring Reactive Web」を選び、Mavenプロジェクトを生成します。以下は依存関係部分の抜粋です。生成された`@SpringBootApplication`付きの起動クラスは残し、各サンプルを同じパッケージに配置してください。Spring Boot 4.1.1の`spring-boot-starter-webflux`は、Jackson、Reactor Netty、`spring-webflux`をまとめて引き込みます。

```
<parent>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>4.1.1</version>
</parent>

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

テスト用の`spring-boot-starter-webflux-test`はSpring Boot 4で追加されたスターターで、`WebTestClient`と`reactor-test`（`StepVerifier`）を含みます。Spring Boot 3までの`spring-boot-starter-test`だけの構成から移行する場合は、これを足します。

### アノテーション方式のコントローラー

Spring MVCと同じ`@RestController`で書けます。この例では戻り値に`Mono`・`Flux`を使います。Spring MVCもこれらの型を扱えるため、戻り値だけでは両者を区別できません。

```
package com.example.demo;

import java.time.Duration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
public class GreetingController {

    @GetMapping("/hello/{name}")
    public Mono<String> hello(@PathVariable String name) {
        return Mono.just("Hello, " + name + " (" + Thread.currentThread().getName() + ")");
    }

    @GetMapping(value = "/ticks", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<Long> ticks() {
        return Flux.interval(Duration.ofSeconds(1)).take(3);
    }
}
```

### 関数型エンドポイント（RouterFunction）の書き方

WebFluxには、ルーティングとハンドラーをラムダで書く関数型エンドポイントもあります。公式リファレンスは、小規模なアプリケーションや要件の単純なマイクロサービスで透明性と制御を重視する場合の選択肢に挙げています。アノテーション方式と同じアプリ内に共存できます。

```
package com.example.demo;

import static org.springframework.web.reactive.function.server.RouterFunctions.route;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
public class GreetingRouter {

    @Bean
    RouterFunction<ServerResponse> greetingRoutes() {
        return route()
                .GET("/fn/hello", request -> ServerResponse.ok()
                        .bodyValue("Hello from functional endpoint"))
                .build();
    }
}
```

### 起動ログとcurlでの動作確認

プロジェクト直下で`./mvnw spring-boot:run`を実行します。Java 25.0.4.1で起動すると、ログに`NettyWebServer : Netty started on port 8080 (http)`と出ます。Tomcatではなく、Nettyが立ち上がったことをここで確認できます。

```
$ curl localhost:8080/hello/issoh
Hello, issoh (reactor-http-nio-2)

$ curl localhost:8080/fn/hello
Hello from functional endpoint

$ curl -N localhost:8080/ticks
data:0

data:1

data:2
```

1つ目の応答に出ている`reactor-http-nio-2`がイベントループのスレッド名です。`/ticks`はServer-Sent Eventsとして1秒ごとに1件ずつ届きます。

## ブロッキング処理の扱い：イベントループを止めない実装と実測

WebFluxで性能問題の原因になりやすいのは、イベントループ上でのブロッキング呼び出しです。JDBC、JPA、`Thread.sleep`、同期HTTPクライアント、ファイルI/Oなどが該当します。16本しかないスレッドのうち1本が止まると、そのスレッドに割り当てられた他のリクエストも待たされます。

1秒かかるブロッキング処理を、イベントループ上で直接実行するエンドポイントと、Reactorの`boundedElastic`スケジューラーへ逃がすエンドポイントを用意し、64リクエストを同時に送って比較しました。

```
@GetMapping("/bad")
public Mono<String> bad() throws InterruptedException {
    Thread.sleep(1000);
    return Mono.just(Thread.currentThread().getName());
}

@GetMapping("/good")
public Mono<String> good() {
    return Mono.fromCallable(() -> {
                Thread.sleep(1000);
                return Thread.currentThread().getName();
            })
            .subscribeOn(Schedulers.boundedElastic());
}
```

| 実装                 | 64件の所要時間   | 処理したスレッド             |
| ------------------ | ---------- | -------------------- |
| イベントループ上で直接ブロック    | 4.60〜5.30秒 | reactor-http-nio 16本 |
| boundedElasticへ逃がす | 1.30〜1.31秒 | boundedElastic 64本   |
| Stringを返す＋仮想スレッド有効 | 1.34秒      | 仮想スレッド 64本           |

計測環境は16論理CPUのMac（x86\_64）、Spring Boot 4.1.1、Java 25.0.4.1です。負荷はPythonの`urllib`と64スレッドの`ThreadPoolExecutor`で64件を同時に送り、全件の応答までの時間を測りました。1行目と2行目は2回ずつ計測した範囲、3行目は1回の値です。直接ブロックした場合、16本のイベントループが1秒ずつ占有されるので、64件は最低でも4巡（約4秒）かかります。`boundedElastic`へ逃がすと、64件がほぼ同時に1秒で終わりました。

3行目は、戻り値を`Mono`にせず`String`を返すコントローラーで、`Thread.sleep(1000)`を呼んだ場合です。Spring Framework 6.1以降のWebFluxは、リアクティブ型以外を返すコントローラーメソッドを「ブロッキング」とみなし、`AsyncTaskExecutor`が設定されていればそちらで実行します。Spring Bootで`spring.threads.virtual.enabled=true`にすると、このメソッドは仮想スレッドで動きました。設定しない場合は同じメソッドが`reactor-http-nio`上で動き、64件に4.75秒かかりました。

ただし、`boundedElastic`は既定でCPUコア数の10倍までしかスレッドを作らない有限のプールです。ブロッキング処理が大半を占めるアプリなら、それはSpring MVCのスレッドプールを手作りしているのと変わりません。逃がすのは一部の遅い呼び出しに限り、DBアクセスそのものをノンブロッキングにしたい場合はR2DBC（`spring-boot-starter-data-r2dbc`）を使います。JPAにはリアクティブ版がないため、「spring boot reactive jpa」という組み合わせはそのままでは成立しません。

## WebClientによる外部API呼び出しとSpring Boot 4の依存変更

WebClientは、WebFluxと同じ`spring-webflux`モジュールに入っているノンブロッキングなHTTPクライアントです。公式リファレンスは、Spring MVCアプリケーションからでもWebClientを使えるとし、リアクティブ化の最初の一歩として勧めています。

Spring Boot 4で注意が必要なのが依存関係です。Spring Boot 4.1.1で`spring-boot-starter-webflux`だけを入れた状態では、`WebClient.Builder`のBeanが0件で、注入するとアプリが起動しません。WebClientの自動構成は`spring-boot-starter-webclient`（中身は`spring-boot-webclient`と`reactor-netty-http`）に分かれており、これを追加するとBeanが1件作られます。Spring Boot 3のサンプルをそのまま写すと、ここでつまずきます。

```
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-webclient</artifactId>
</dependency>
```

```
package com.example.demo;

import java.time.Duration;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

@Component
public class GreetingClient {

    private final WebClient webClient;

    public GreetingClient(WebClient.Builder builder) {
        this.webClient = builder.baseUrl("http://localhost:8080").build();
    }

    public Mono<String> fetch(String name) {
        return webClient.get()
                .uri("/hello/{name}", name)
                .retrieve()
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(2))
                .onErrorResume(java.util.concurrent.TimeoutException.class,
                        e -> Mono.just("fallback"));
    }
}
```

ブロッキングなSpring MVCアプリで同期的にHTTPを呼ぶだけなら、WebClientより`RestClient`が素直です。使い分けと`RestTemplate`からの移行は[SpringのRestClientの使い方](/tech/details/3311/)で解説しています。

## WebFluxを採用すべき場面と避けるべき場面

公式リファレンスの「Applicability」は、動作しているSpring MVCアプリケーションを変える必要はないと明言しています。さらに、幅広いアプリケーションで移行は不要だろうと述べています。WebFluxの採用は「新しいから」ではなく、次の条件で判断します。

### WebFluxが向いている条件

- 外部APIやマイクロサービスへの呼び出しが多く、1回の待ち時間が長い、または呼び出し同士の依存が深い（公式は、この2つが大きいほど効果も大きいとしています）。
- Server-Sent EventsやWebSocketで、多数のクライアントへ長時間ストリームを流す。
- Spring Cloud Gatewayのように、I/O待ちが処理の大半を占めるゲートウェイやプロキシを作る。
- DBがR2DBC対応（PostgreSQL、MySQL、SQL Serverなど）で、データアクセスまでノンブロッキングで通せる。

### WebFluxを避けるべき条件

- JPA・JDBC・同期SDKなどブロッキングなライブラリが中心。公式も、この場合は少なくとも一般的な構成ではSpring MVCが最適としています。
- チームにリアクティブの経験者がいない大規模開発。公式は、ノンブロッキング・関数型・宣言型への移行は学習曲線が急だと注意しています。スタックトレースが追いにくく、障害調査の難度も上がります。
- CPU負荷の高い計算が中心で、I/O待ちが少ない。WebFluxに変えても計算量は減らず、重い計算をイベントループ上で実行すると他のリクエスト処理を遅らせます。

### Spring MVCの仮想スレッド利用とWebFluxとの選択基準

Java 21以降なら、Spring MVCのまま`spring.threads.virtual.enabled=true`を設定して仮想スレッドを使う方法もあります（[Spring Bootリファレンスの「Virtual threads」](https://docs.spring.io/spring-boot/reference/features/spring-application.html)）。ブロッキングな書き方のまま、待ち時間中にOSスレッドを手放せるので、「I/O待ちでスレッドが足りない」という課題の多くはこれで解決します。Spring Bootのリファレンスは、Java 24以降を強く推奨しています。また、仮想スレッドがキャリアスレッドに固定される「ピン留め」でスループットが落ちる場合があると注意しています。

前章の実測のとおり、WebFluxのアプリでも仮想スレッドを有効にすれば、`String`などを返すブロッキングなコントローラーだけを仮想スレッドへ回せます。既存のMVCアプリの同時接続数を伸ばしたいなら仮想スレッド、ストリーミングやバックプレッシャー、非同期呼び出しの合成そのものが要件ならWebFlux、という線引きが実用的です。仮想スレッドの仕組みとピン留めの落とし穴は[Java Virtual Thread（仮想スレッド）の解説](/tech/details/3209/)を参照してください。

## WebTestClientとStepVerifierによるテスト

WebFluxのコントローラーは`@WebFluxTest`と`WebTestClient`で、サーバーを起動せずにテストできます。Spring Boot 4では、`@WebFluxTest`のパッケージが`org.springframework.boot.webflux.test.autoconfigure`に変わりました。

```
package com.example.demo;

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.webflux.test.autoconfigure.WebFluxTest;
import org.springframework.test.web.reactive.server.WebTestClient;

@WebFluxTest(GreetingController.class)
class GreetingControllerTest {

    @Autowired
    WebTestClient client;

    @Test
    void hello() {
        client.get().uri("/hello/issoh").exchange()
                .expectStatus().isOk()
                .expectBody(String.class)
                .value(body -> assertThat(body).startsWith("Hello, issoh"));
    }
}
```

`Mono`や`Flux`そのものを検証するときは、`reactor-test`の`StepVerifier`を使います。流れてくる要素と完了シグナルを順番どおりに検証できます。

```
@Test
void mapAndFilter() {
    Flux<String> flux = Flux.range(1, 5)
            .filter(n -> n % 2 == 1)
            .map(n -> "item-" + n);

    StepVerifier.create(flux)
            .expectNext("item-1", "item-3", "item-5")
            .verifyComplete();
}
```

この記事のサンプルを含む検証用プロジェクトは、Spring Boot 4.1.1とJava 25.0.4.1の環境で`mvn test`を実行し、テスト5件がすべて成功することを確認済みです。

## よくある質問

### Spring WebFluxとは何ですか？

Spring Frameworkに含まれるノンブロッキングなWebフレームワークです。Spring Framework 5.0で追加され、Reactive StreamsとProject Reactorを基盤に、少数のイベントループスレッドで多数のリクエストを処理します。

### Spring WebFluxとSpring MVCの違いは何ですか？

前提となるスレッドモデルが違います。Spring MVCはリクエストごとにスレッドを占有しブロッキングを許容し、WebFluxはスレッドを止めない前提でイベントループを使います。アノテーションは共通で、`Mono`・`Flux`も両方で使えます。WebFluxでは通常の値を返すこともできます。

### Spring WebFluxの既定のサーバーは何ですか？

Netty（Reactor Netty）です。Spring Boot 4.1.1の`spring-boot-starter-webflux`はReactor Netty 1.3.7とNetty 4.2.17.Finalを引き込みます。TomcatやJettyに切り替えることもできます。

### Spring WebFluxでJPAは使えますか？

技術的には`boundedElastic`などへ逃がせば呼べますが、ノンブロッキングの利点は失われます。JPAが中心なら公式もSpring MVCを推奨しており、リレーショナルDBへのアクセスまでノンブロッキングにするなら、対応ドライバーのあるR2DBC（Spring Data R2DBC）が候補です。

### MonoとFluxはどう使い分けますか？

結果が0〜1件なら`Mono`、0〜N件やストリームなら`Flux`を使います。IDで1件取得する処理は`Mono`、一覧取得やServer-Sent Eventsは`Flux`が典型です。

## 関連記事

- [Spring MVCとは？spring-webmvcの役割とSpring Boot 4での依存名変更を解説【2026年最新】](/tech/details/10533/)
- [Java Virtual Thread（仮想スレッド）とProject Loomとは？作り方・仕組み・落とし穴を実務目線で解説](/tech/details/3209/)
- [SpringのRestClientとは？使い方とRestTemplate非推奨後の移行【Spring Boot 4対応】](/tech/details/3311/)
- [バックプレッシャーとは？流量制御の仕組みと実装方式の選び分けを実装視点で解説](/tech/details/16432/)
- [Spring Boot 4とは？最新バージョン4.1の変更点・新機能とSpring Boot 3との違いを解説【2026年最新】](/tech/details/10124/)

---

出典: [Spring WebFluxとは？MVCとの違い・Mono/Fluxの基本とSpring Boot 4.1での使い方](<https://www.issoh.co.jp/tech/details/3208/>)（株式会社一創）
