前回、アプリケーションの自動化を実現するスケジューリング機能について学びました。
Webアプリケーションは、単独で完結することは少なく、決済サービス、気象情報、認証プロバイダなど、**外部のWeb API(Webサービス)**と連携するのが一般的です。
今回は、Spring Bootで外部APIと連携する際の標準的なHTTPクライアントとして推奨されている WebClient の基本的な使い方と、その大きな特徴である**リアクティブ(非同期・ノンブロッキング)**な通信方法を学びます。
1. WebClientとは?(なぜRestTemplateではないのか)
以前のSpringでは、HTTP通信を行うために RestTemplate が使われていました。しかし、RestTemplateは同期処理(ブロッキングI/O)が基本であり、通信中にスレッドが待機してしまうため、大規模なアプリケーションでは性能上のボトルネックになりやすい問題がありました。
WebClient は、この問題を解決するために登場しました。
- 非同期・ノンブロッキング: 通信の完了を待たずに処理を次に進めることができ、スレッドのブロックを防ぎます。これは、第31回で学んだ
@Asyncよりもさらに効率的な通信方法です。 - リアクティブ: Spring WebFluxというリアクティブフレームワークの一部ですが、Spring MVCアプリケーション(これまでに開発してきたアプリケーション)でも問題なく利用できます。
注意: WebClientを使うには、
spring-boot-starter-webfluxの依存関係をプロジェクトに追加する必要があります。
2. WebClientの基本的な使い方
WebClientは、ビルダーパターンを使ってインスタンスを生成し、Fluent API(メソッドをチェーンで繋いでいく記述)でリクエストを構築するのが特徴です。
2-1. WebClientのBean定義
外部APIのベースURLを設定した WebClient のインスタンスを Bean として登録しておくと便利です。
Java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
public class WebClientConfig {
@Bean
public WebClient externalApiClient(WebClient.Builder builder) {
// 頻繁にアクセスする外部サービスのベースURLを設定
return builder.baseUrl("https://api.external-service.com")
.build();
}
}
2-2. データの取得(GETリクエスト)
Service層で WebClient をDIで受け取り、リクエストを実行します。
Java
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import lombok.RequiredArgsConstructor;
import reactor.core.publisher.Mono; // 非同期の結果を扱うためのクラス
@Service
@RequiredArgsConstructor
public class ExternalApiService {
private final WebClient externalApiClient; // DIで注入
public Mono<ExternalData> fetchData(String path) {
// 1. リクエストの構築
return externalApiClient.get() // GETリクエスト
.uri("/v1/data/{path}", path) // パスとURI変数を設定
.retrieve() // リクエスト実行
// 2. レスポンスの処理
.bodyToMono(ExternalData.class) // レスポンスボディを ExternalData クラスにマッピング
// 3. エラーハンドリング(例: 4xx, 5xx ステータスコードの場合)
.doOnError(e -> System.err.println("API通信エラー: " + e.getMessage()));
}
}
3. リアクティブな戻り値:MonoとFlux
WebClientの最大の特徴は、戻り値の型が Mono または Flux になることです。これらは、リアクティブプログラミングにおけるデータストリームを表現します。
| 戻り値の型 | 意味 |
| Mono | 0件または1件のデータストリーム(非同期の結果) |
| Flux | 0件以上のデータストリーム(非同期のリスト、または継続的なデータ) |
ControllerやServiceでは、この Mono<T> や Flux<T> を受け取ります。
3-1. Mono/Fluxの処理
この Mono や Flux からデータを取り出すには、**購読(Subscribe)**という操作が必要ですが、Spring MVCのControllerが戻り値としてこれらを受け取った場合、Springが自動的に購読処理を行い、データが到着次第、クライアントにレスポンスを返します。
Java
@RestController
@RequiredArgsConstructor
public class DataController {
private final ExternalApiService apiService;
// Controllerの戻り値を Mono<T> にする
// WebClientはノンブロッキングでデータを取得し、
// 取得が完了した時点でSpringがレスポンスを自動で構築する
@GetMapping("/data/{id}")
public Mono<ExternalData> getExternalData(@PathVariable String id) {
return apiService.fetchData(id);
}
// Listを返す場合は Flux を使う
@GetMapping("/data/all")
public Flux<ExternalData> getAllExternalData() {
return apiService.fetchAllData();
}
}
開発者は、Mono や Flux をチェーンで繋いで処理を記述する(例: map や filter)ことで、同期処理の記述感覚で非同期通信を実現できます。
4. データの送信(POSTリクエスト)
データの送信(POST/PUT)も同様に、bodyValue() や bodyToMono() を使って行います。
Java
public Mono<ApiResponse> postData(RequestData data) {
return externalApiClient.post() // POSTリクエスト
.uri("/v1/submit")
.bodyValue(data) // 送信するリクエストボディを設定
.retrieve()
.bodyToMono(ApiResponse.class);
}
✅ 本日のまとめ
- WebClientは、Spring Bootで推奨されるHTTPクライアントであり、非同期・ノンブロッキング通信を実現し、アプリケーションの性能低下を防ぐ。
- WebClientを使うには、
spring-boot-starter-webfluxの依存関係が必要である。 - WebClientは、ビルダーパターンとメソッドチェーン(
get()->uri()->retrieve()->bodyToMono())でリクエストを構築する。 - 非同期の結果は
Mono(0〜1件)またはFlux(0件以上)というリアクティブな型で返される。 - Spring MVCのControllerは、
MonoやFluxを戻り値として受け取ると、自動で購読し、データが揃い次第レスポンスを返す。
🔔 次回予告
外部APIとの連携ができるようになりましたが、APIが混雑している場合や、一時的なネットワーク障害が発生した場合、処理が失敗したり、クライアントを長時間待たせたりする可能性があります。
次回は、外部サービスとの通信の安定性を高めるため、リトライ(再試行)やサーキットブレーカーといった耐障害性パターンを実装する方法について学びます。
次回:【第34回】外部連携の安定化!〜リトライとサーキットブレーカー〜 にご期待ください!


コメント