【SpringBoot】RestTemplateからWebClientに移行

はじめに

お世話になります、hosochinです
Spring MVCでWebAPIをコールする場合によく使われるRestTemplateですが、現在メンテナンスモードとなっていて、

注意 : 5.0 の時点では、このクラスはメンテナンスモードになっており、今後は変更やバグを受け入れるためのマイナーなリクエストのみが行われます。最新の API を備え、同期、非同期、ストリーミングのシナリオをサポートする org.springframework.web.reactive.client.WebClient の使用を検討してください。

とのことだったので、spring-web(RestTemplate)からspring-webflux(WebClient)に移行しようと思ったときのメモになります

この記事についての注意

この記事ではspring-webfluxの特徴であるリアクティブプログラミングによるノンブロッキングな書きっぷりにはしていないです
従来のspring-web的なブロッキング処理をspring-webfluxで書いただけなので注意してください
また、本来であればspring-webfluxでブロッキング処理を使用したい場合は、publishOn関数やsubscribeOn関数を使うべきです
こちらの記事にあるように、webfluxでブロッキング処理を安易に実装するのは危険です(spring-webfluxでは、デフォルトで使用可能なリクエストを処理するスレッドが少ないため、すぐに詰まってしまう可能性があるようです)

サンプルコード

外部APIにGET, POSTする場合に

  • RestTemplateでの実装
  • WebClientでの実装

についてまとめました

  • build.gradle
dependencies {
	implementation('org.springframework.boot:spring-boot-starter-web') // spring-web
	implementation('org.springframework.boot:spring-boot-starter-webflux') // spring-webflux
}
  • コントローラ
@RestController
@RequiredArgsConstructor
public class SampleController {

    private final TutorialRestTemplate tutorialRestTemplate;
    private final TutorialWebClient tutorialWebClient;

    // RestTemplateのGET
    @GetMapping("/restTemplate")
    public Message getRestTemplate() {
        return tutorialRestTemplate.get();
    }

    // RestTemplateのPOST
    @PostMapping("/restTemplate")
    public void postRestTemplate(@RequestBody Message message) {
        tutorialRestTemplate.post(message);
    }

    // WebClientのGET
    @GetMapping("/webClient")
    public Message getWebClient() {
        return tutorialWebClient.get();
    }

    // WebClientのPOST
    @PostMapping("/webClient")
    public void postWebClient(@RequestBody Message message) {
        tutorialWebClient.post(message);
    }
}

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Message {
    private String value;
}

外部APIを用意する

こちらを参照してくださいmm
localhost:3000/usersにGET,POSTのエントリポイントを用意します

RestTemplateの場合

@Component
public class TutorialRestTemplate {

    private final RestTemplate restTemplate;

    // コンストラクタインジェクション
    public TutorialRestTemplate(RestTemplateBuilder restTemplateBuilder) {
        this.restTemplate = restTemplateBuilder.build();
    }

    // GETメソッド
    public Message get() {
        return restTemplate.getForObject("http://localhost:3000/users", Message.class);
    }

    // POSTメソッド
    public void post(Message message) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<Message> request = new HttpEntity<>(message, headers);
        restTemplate.postForObject("http://localhost:3000/users", request, String.class);
    }
}

WebClientの場合

@Component
public class TutorialWebClient {

    private final WebClient webClient;

    // コンストラクタインジェクション
    public TutorialWebClient() {
        this.webClient = WebClient.builder()
                .baseUrl("http://localhost:3000")
                .build();
    };

    // GETメソッド
    public Message get() {
        return webClient.get()
                .uri("/users")
                .retrieve() // retrieveの後にレスポンスを抽出する方法を記述する
                .bodyToMono(Message.class) // Message型で受け取る
                .block(); // ブロッキング
    }

    // POSTメソッド
    public void post(Message message) {
        webClient.post()
                .uri("/users")
                .contentType(MediaType.APPLICATION_JSON) // リクエストボディのタイプ
                .body(Mono.just(message), Message.class) // リクエストボディ
//                .syncBody(message)
                .retrieve()
                .bodyToMono(String.class)
                .block(); // ブロッキング
    }
}

解説

RestTemplateを使った処理をWebClientで書き換えてみました
ですが冒頭の注意書きで示したとおり、webfluxを利用する場合は本来ブロッキング処理を持たせるべきではないです
ノンブロッキングで実装する場合はコントローラの戻り値をPublisher型(Mono型、Flux型)とし、関数型プログラミングによって処理を構築する必要があります(なのでRestTemplate→WebClientへの移行は場合によったらだいぶ大掛かりな改修になりそうです。。)
今回はひとまずWebClientを使った同期処理に書き換えてみるという部分に焦点を置いてみました
今後はspring-webfluxでのノンブロッキングな実装についても記事にできたらなあと思います😎

参考

https://spring.pleiades.io/spring-framework/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html
https://spring.pleiades.io/spring-framework/docs/current/reference/html/web-reactive.html
https://www.alpha.co.jp/blog/202105_02
https://yuya-hirooka.hatenablog.com/entry/2020/10/03/095015
https://qiita.com/kilvis/items/fb18be963da6cac03ee9

技術Java,spring,SpringBoot

Posted by hosochin