WebFluxのセッションを実装してみる


引き続きWebFluxの話です。
今回はWebFluxでのSessionの使い方を紹介します。
なお、ここで紹介するのは最小構成のRedisを使わないインメモリの実装例です。

サンプルの環境

  • Java 8
  • Spring Boot 2.2.6

プロジェクトのビルド構成

Mavenの場合、基本編のビルド構成に以下のdependencyを追加します。

pom.xml
〜 省略 〜
<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-core</artifactId>
</dependency>

Gradleの場合は以下のdependenciesを参考にしてください。

build.gradle
〜 省略 〜
dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
    implementation 'org.springframework.session:spring-session-core'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'
}

セッションを使うための設定

まずWebFluxでセッションを使うためにSessionRepositoryのDIの設定を行います。

InMemorySessionConfig.java
@Configuration
@EnableSpringWebSession    // 1
public class InMemorySessionConfig {

  @Bean
  public ReactiveSessionRepository reactiveSessionRepository() {

    ReactiveMapSessionRepository repository =
        new ReactiveMapSessionRepository(new ConcurrentHashMap<>());    // 2

    repository.setDefaultMaxInactiveInterval(60 * 30); // 3

    return repository;
  }

}

ポイントはコメントの3点
1. @EnableSpringWebSession アノテーションを付与しWebSessionの有効化
2. ReactiveSessionRepository のBean登録
3. セッションの有効期限(秒)の指定 (例では 60秒*10 = 10分)

セッションを利用してみる

それでは、実際にセッションを使った簡単なカウンタの実装例を見てみましょう。

CountController.java

@RestController
public class CountController {

  @GetMapping("count/{increments}")
  public Mono<Integer> count(@PathVariable Integer increments, WebSession webSession) {    // 1
    return Mono.fromSupplier(
        () -> {
          Integer total =
              Optional.ofNullable((Integer) webSession.getAttribute("count"))
                  .map(current -> current + increments)
                  .orElse(0);    // 2
          webSession.getAttributes().put("count", total);    // 3
          return total;
        });
  }
}

WebFluxでセッションにアクセスするには、WebSessionを使います。
ただし、Servletの HttpSession のようなフィールドインジェクションは行えないため、以下のような実装になります。

  1. エンドポイントのメソッド引数に WebSession を指定
  2. セッションから現在のカウントを取り出し、あればincrementsを加算し、なければ初期値0をtotalにセット
  3. 計算結果のtotalをセッションに詰める

以上の実装で、
localhost:8080/count/1
に連続でアクセスするたびに1ずつカウントアップする動作が確認できます。
また、10分間アクセスがなかった場合は、初期値の0に戻ります。

ちょっと使いづらい・・・

セッションの値を使いたい場合、ControllerでWebSessionを取得し、後続処理(Serviceなど)へ値を渡していかなければならず、

  • Servletの実装のようにSessionScopeのBeanを定義したり、
  • 任意のComponentのフィールドやコンストラクタでHttpSessionをDIしたり、

といった使い方ができないので、セッションを扱うComponentの設計には注意が必要です。

(そもそも、SessionScopeやRequestScopeはThreadLocalに依存しているので、WebFluxで同様に扱えないのは仕方がないのかもしれません^^;)

参考

https://docs.spring.io/spring-session/docs/current/reference/html5/#websession
→公式にRedisとWebSessionの連携方法も載っていますので参考まで。