🛠️ Spring Framework 講座【第44回】処理の共通化と横断的関心事!〜インターセプター(Interceptor)の活用〜

docs

前回、Spring Boot Actuatorを使ってアプリケーションの運用監視を容易にする方法を学びました。

Webアプリケーション開発では、特定のControllerやメソッドだけでなく、全てのリクエストに対して共通で実行したい処理が多く存在します。例えば、「ログイン状態のチェック」「リクエストの処理時間計測」「ロギング」などです。

今回は、Spring MVCが提供する強力な仕組みであるインターセプター(Interceptor)について学びます。インターセプターは、リクエスト処理の前、中、後に共通ロジックを挿入するための設計パターンです。


1. インターセプターとは?

インターセプターは、Controllerがリクエストを処理するまでの流れ(処理パイプライン)に介入し、**横断的な関心事(Cross-Cutting Concerns)**を処理するために使われます。

  • 横断的関心事: アプリケーションの複数の場所で共通して必要となる機能のこと。セキュリティ、ロギング、トランザクション管理、性能計測などがこれにあたります。

インターセプターを適切に使うことで、各Controllerメソッドに同じコードを繰り返し書く必要がなくなり、コードの**重複(Duplication)**を防ぎ、保守性が向上します。

2. インターセプターの実装

インターセプターを作成するには、HandlerInterceptor インターフェースを実装します。このインターフェースは、リクエストのライフサイクルに応じた3つの主要なコールバックメソッドを提供します。

メソッド実行タイミング戻り値用途
preHandleController実行前boolean認証チェック、ロギング、リクエスト処理の開始時刻記録など。false を返すと処理を中断。
postHandleController実行後(Viewレンダリング前)void処理時間の記録、Viewに渡す共通データの追加など。
afterCompletionレスポンス送信後(Viewレンダリング後)voidリソースの解放、エラーログの記録など、必ず実行したい後処理。

2-1. 処理時間計測インターセプターの例

Java

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class PerformanceInterceptor implements HandlerInterceptor {
    
    // ① Controller実行前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 処理開始時刻をリクエスト属性に保存
        long startTime = System.currentTimeMillis();
        request.setAttribute("startTime", startTime);
        return true; // 処理を続行
    }
    
    // ② Controller実行後 (postHandle)
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 処理時間を計算
        long startTime = (Long) request.getAttribute("startTime");
        long endTime = System.currentTimeMillis();
        long executionTime = endTime - startTime;

        System.out.println(request.getRequestURI() + " の処理時間: " + executionTime + "ms");
    }

    // ③ レスポンス送信後 (afterCompletion)
    // エラー発生時でも呼び出される
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 例外が発生した場合の最終的なロギングなど
        if (ex != null) {
            System.err.println("リクエスト中にエラーが発生: " + ex.getMessage());
        }
    }
}

3. インターセプターの登録

インターセプターを作成しただけでは機能しません。Spring MVCに「どのパスに、どのインターセプターを適用するか」を登録する必要があります。

これは、WebMvcConfigurer インターフェースの addInterceptors メソッドをオーバーライドして行います。

Java

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        
        // 1. PerformanceInterceptorを登録
        registry.addInterceptor(new PerformanceInterceptor())
                // 2. 適用するURLパターンを設定
                .addPathPatterns("/api/**") 
                // 3. 除外するURLパターンを設定 (例: ヘルスチェックは除く)
                .excludePathPatterns("/actuator/**"); 

        // 複数のインターセプターを登録可能。登録順に preHandle が実行される。
        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns("/admin/**");
    }
}

4. フィルターとの違い

インターセプターと同じく、リクエストを横断的に処理する仕組みとして**サーブレットフィルター(Filter)**があります。両者は似ていますが、役割と適用範囲が異なります。

項目インターセプター (Interceptor)フィルター (Filter)
適用層Spring MVC層(Controllerに処理が渡される前)サーブレットコンテナ層(Spring MVCの起動前)
介入範囲Spring MVCの内部機能(HandlerMethod, ModelAndViewなど)にアクセス可能。HTTPリクエスト/レスポンスの低レベルな操作(エンコーディング、圧縮、認証トークンの抽出など)
依存関係SpringのDIコンテナのBeanを使用可能。一般的にSpringのBeanを使わないことが多い。

使い分けの目安: Spring MVCの機能(Controller情報、View情報)が必要な場合はインターセプター。リクエストの文字エンコーディングや、Spring Securityが処理する前の生のHTTPリクエストを操作したい場合はフィルターを使います。

✅ 本日のまとめ

  • インターセプターは、リクエスト処理の前、中、後に、横断的な関心事(ロギング、性能計測など)を挿入し、コードの重複を防ぐ。
  • HandlerInterceptorを実装し、preHandlepostHandleafterCompletionの3つのメソッドで処理を記述する。
  • WebMvcConfigurer の addInterceptors メソッドを使って、URLパターンを指定してインターセプターを登録する。
  • フィルターはSpring MVCより前の層(サーブレットコンテナ)で動作するのに対し、インターセプターはSpring MVCの内部に介入する。

🔔 次回予告

これで、Spring Frameworkを使った開発の主要なトピックはほぼ網羅できました。ここからは、より高度な設計とパフォーマンスチューニングのトピックに入ります。

次回は、Webアプリケーションの性能を劇的に向上させるためのテクニック、キャッシュについて学びます。Spring Bootが提供する @Cacheable アノテーションの基本的な使い方と、外部キャッシュシステム(Redisなど)との連携の基本を解説します。

次回:【第45回】アプリケーションの応答速度向上!〜Spring Cacheの基本と活用〜 にご期待ください!

コメント

タイトルとURLをコピーしました