Spring BootでRedisを使う方法 - セッション管理・キャッシュ・Pub/Subの実装パターン


Redisの名前はキャッシュ関連の記事でよく見かけるけれど、「実際にSpring Bootでどう使うの?」となかなか踏み出せていない方も多いですよね。

この記事では、Docker ComposeでRedisをローカルに立ち上げるところから始めて、RedisTemplateの基本操作、Spring Sessionによるセッション外部化、@Cacheableのバックエンド設定、Pub/Sub実装まで用途別にコードを示しながら解説します。対象はSpring Boot 3.x / Java 17以上です。Redisの運用・チューニング・Cluster構成は扱いません。

Docker ComposeでRedisをローカルに立ち上げる

まずはローカル環境にRedisを用意しましょう。

services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

docker compose up -d で起動後、docker compose exec redis redis-cli ping を実行して PONG が返れば準備完了です。Spring Bootコンテナと同じCompose内に配置する場合は、ホスト名にサービス名 redis をそのまま使えます。コンテナ化の詳細は Spring BootアプリをDockerでコンテナ化する方法 も参考にしてください。

依存追加と接続設定

build.gradlespring-boot-starter-data-redis を追加します。

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}

application.yml の接続設定はシンプルです。

spring:
  data:
    redis:
      host: localhost  # Docker Compose内ならサービス名 "redis"
      port: 6379
      # password: your-password
      connect-timeout: 2s
      timeout: 1s

/actuator/health を有効にしておくと、起動時にRedisへの疎通をまとめて確認できて便利です。

RedisTemplateの基本操作

文字列だけ扱う場合は StringRedisTemplate が手軽です。

@Service
@RequiredArgsConstructor
public class CacheService {

    private final StringRedisTemplate redisTemplate;

    public void save(String key, String value, long ttlSeconds) {
        redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttlSeconds));
    }

    public String get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    public void delete(String key) {
        redisTemplate.delete(key);
    }

    public void updateExpire(String key, long ttlSeconds) {
        redisTemplate.expire(key, Duration.ofSeconds(ttlSeconds));
    }
}

POJOも保存したい場合は RedisTemplate<String, Object> を使います(次節で設定します)。

ObjectのシリアライズにGenericJackson2JsonRedisSerializerを使う

デフォルトの JdkSerializationRedisSerializer はRedis上のデータが読みにくく、クラスのバージョン変更にも弱いです。POJOを保存するなら GenericJackson2JsonRedisSerializer を使いましょう。保存されるJSONには @class フィールドが自動付与されるため、デシリアライズ時に型を指定しなくて済みます。一方でクラスのリネーム・パッケージ移動で既存データとの互換性が崩れる点は注意してください。

カスタム ObjectMapper を渡す場合は activateDefaultTyping() を明示しないとPOJOが LinkedHashMap として返されてしまいます。これを追加することでPOJOの型情報がJSONに埋め込まれ、取り出し時に正しく型復元されます。なお文字列のみ保存するケースでは Jackson2JsonRedisSerializer で十分です。

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        var template = new RedisTemplate<String, Object>();
        template.setConnectionFactory(factory);

        var objectMapper = new ObjectMapper()
            .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .setSerializationInclusion(JsonInclude.Include.NON_NULL)
            .activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL
            );
        var serializer = new GenericJackson2JsonRedisSerializer(objectMapper);

        template.setDefaultSerializer(serializer);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashValueSerializer(serializer);
        return template;
    }
}

Spring SessionでセッションをRedisに外部化する

複数インスタンスにスケールアウトすると、サーバー内メモリのセッションは共有されません。Spring Session + Redisで解決しましょう。

spring-boot-starter-data-redis が追加済みであることを前提に、追加で以下の依存を加えます。

implementation 'org.springframework.session:spring-session-data-redis'

Spring Boot 3.x では application.yml への設定追加だけでセッション外部化が有効になります。これが最もシンプルな方法です。

spring:
  session:
    store-type: redis
    timeout: 30m

タイムアウトや設定をコードで細かく制御したい場合は @EnableRedisHttpSession を使うこともできます。ただし Spring Session 3.x では @EnableRedisIndexedHttpSession が新しい推奨アノテーションです。application.yml の auto-configuration と競合しないよう、どちらか一方に統一しましょう。

これでどのインスタンスにリクエストが届いてもセッションを正しく読み出せるようになります。JWTによるステートレス認証については Spring Security + JWT認証の実装 も参照してみてください。

@CacheableのバックエンドにRedisを使う

RedisCacheManager を定義すれば @Cacheable のバックエンドをRedisにできます。

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        var defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(10))
            .disableCachingNullValues();

        var perCacheConfig = Map.of(
            "products", defaultConfig.entryTtl(Duration.ofHours(1)),
            "rankings", defaultConfig.entryTtl(Duration.ofMinutes(1))
        );

        return RedisCacheManager.builder(factory)
            .cacheDefaults(defaultConfig)
            .withInitialCacheConfigurations(perCacheConfig)
            .build();
    }
}

キャッシュ名ごとに個別のTTLを設定できるのが便利です。@Cacheable の詳しい使い方は Spring Cacheの@Cacheableを使ったキャッシュ実装 にまとめています。

Pub/SubでRedis経由のメッセージ送受信を実装する

Redis Pub/Subはシンプルな通知・イベント配信に使えます。永続化されないため、サブスクライバーが接続していない間のメッセージは消えてしまう点は把握しておきましょう。

@Slf4j
@Component
public class NotificationListener {
    public void handleMessage(String message) {
        log.info("受信: {}", message);
    }
}

@Configuration
public class PubSubConfig {

    @Bean
    public RedisMessageListenerContainer listenerContainer(
            RedisConnectionFactory factory, MessageListenerAdapter adapter) {
        var container = new RedisMessageListenerContainer();
        container.setConnectionFactory(factory);
        container.addMessageListener(adapter, new PatternTopic("notification:*"));
        return container;
    }

    @Bean
    public MessageListenerAdapter messageListenerAdapter(NotificationListener listener) {
        return new MessageListenerAdapter(listener, "handleMessage");
    }
}

RedisTemplate<String, Object> ではシリアライザーが介在し、メッセージに型情報のJSONクォートが付与されてしまうため、送信には StringRedisTemplateconvertAndSend() を使いましょう。

// StringRedisTemplateを使うこと(RedisTemplate<String,Object>はNG)
stringRedisTemplate.convertAndSend("notification:user", "ログインしました");

チャンネル名はプレフィックスで用途を区別すると管理しやすくなります。本格的な非同期処理が必要な場合は Spring Bootの非同期処理 も合わせて参照してください。

ReactiveRedisTemplate(WebFluxユーザー向け)

WebFluxを使っている場合は ReactiveRedisTemplate が利用できます。opsForValue().set()Mono<Boolean> を返す点が命令型と異なります。

Spring MVC アプリでも ReactiveRedisTemplate は自動構成されますが、実用的なメリットは WebFlux プロジェクトで Reactor の Mono/Flux チェーンに組み込む場合に限られます。spring-boot-starter-webflux 依存が既に存在する場合、ReactiveRedisConnectionFactory は auto-config されるため追加のBean定義は不要です。通常のSpring MVCアプリなら RedisTemplate で十分です。

接続エラーが出たときの確認ポイント

起動時に RedisConnectionFailureException が出た場合はこの順で確認してください。

  1. application.yml のホスト名・ポートが正しいか
  2. Redisコンテナが起動しているか(docker compose ps
  3. Spring BootとRedisが同じDockerネットワーク上にあるか
  4. /actuator/health でRedisのステータスを確認する

ClassCastException が出る場合はシリアライザーの設定変更後に古いデータが残っている可能性があります。開発環境に限り redis-cli FLUSHDB でDB全体をクリアすると解消することが多いです。本番環境では DEL <key> で特定キーを削除しましょう。

まとめ

Spring BootでRedisを使う主なパターンを用途別に紹介しました。

  • RedisTemplate で文字列・オブジェクトのCRUD操作
  • Spring Session でセッションを外部化してスケールアウト対応
  • RedisCacheManager@Cacheable のバックエンドとして利用
  • Pub/Sub でシンプルなイベント通知を実装

まずは StringRedisTemplateset/get から試してみるのがおすすめです。スケールアウトが必要になったら Spring Session を追加し、パフォーマンスが気になってきたら RedisCacheManager を導入するのが自然な順序です。