はじめに
なんだかinterceptorという便利な機能もあるようなので、今日はそれについてまとめてみます。
interceptorとは
割り込み?遮り?
コードと動きを見た方が早そうですね。
こんなことをやってみる
interceptorを使うとこんなことができるらしいです。
つかってみる。
APIの準備
いつもの如く、適当なAPIでも用意しましょう。
@RestController
@RequestMapping("/api")
public class SampleApi {
@GetMapping("/hello")
public ResponseEntity<String> getHello() {
System.out.println("Hello, World!");
return ResponseEntity.ok("Hello, World!");
}
}
interceptorの実装
次にinterceptorを用意しましょう。前処理と後処理を実装します。
public class LoggingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
System.out.println("前処理で開始します!");
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
System.out.println("後処理で終わります!");
}
}
登録する
interceptorの実行を登録するらしいです。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor());
}
}
動かしてみる
こんなのがログに出ました。
前処理で開始します!
Hello, World!
後処理で終わります!
interceptorを利用して、こんな処理ができました。
複数登録する
複数のinterceptorを登録することもできます。それぞれ役割の違う処理を入れてみましょう。
// 時間を計測するinterceptor
public class TimingInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) {
request.setAttribute("startTime", System.currentTimeMillis());
System.out.println("時間計測開始!");
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
long startTime = (Long) request.getAttribute("startTime");
long endTime = System.currentTimeMillis();
System.out.println("処理時間: " + (endTime - startTime) + "ms");
}
}
// 登録を追加
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor());
registry.addInterceptor(new TimingInterceptor());
}
}
こんなログが出力されます:
前処理で開始します!
時間計測開始!
Hello, World!
処理時間: 5ms
後処理で終わります!
特定の場合のみ登録する
特定のパスにのみ適用することができます。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoggingInterceptor())
.addPathPatterns("/api/**") // /api/で始まるパスに適用
.excludePathPatterns("/api/public/**"); // /api/public/は除外
registry.addInterceptor(new TimingInterceptor())
.addPathPatterns("/api/hello"); // /api/helloのみに適用
}
}
このように設定することで:
- LoggingInterceptorは
/api/
で始まるパスすべてに適用し、そのなかで/api/public/
は除外 - TimingInterceptorは
/api/hello
のみに適用
ということで、パブリックに公開するAPIと、ログイン済みユーザーのみ使用できるAPIで分けることができそうですね。
え、フィルターと何が違うの?
そうなんです。フィルターという似たような機能もあるんですよね。
違いを見ていきましょう。
実行されるタイミングが違う
フィルターとinterceptorでは、実行されるタイミングが違うんです。
どっちを使えばいいの?
調べてみると、以下のような使い分けがあるようです。
- filter
- 全体的な共通処理
- interceptor
- 機能別の共通処理
よくわかんないけど、、、AIに聞いていたら。。。
具体的なユースケース例を見てみましょう:
フィルターの使用例:
- 文字エンコーディングの設定
- CORS(Cross-Origin Resource Sharing)の設定
- セキュリティフィルター(XSS対策など)
- リクエスト/レスポンスのログ記録
- 認証トークンの検証
インターセプターの使用例:
- ユーザーの権限チェック
- APIの実行時間の計測
- トランザクション管理
- 監査ログの記録
- ビジネスロジック実行前後の共通処理
つまり、フィルターは主にWebアプリケーション全体に関わる基盤的な処理を担当し、インターセプターはSpring MVCのコンテキスト内での機能単位の処理を担当すると考えるとよいでしょう。
また、技術的な観点からも以下の違いがあります:
1. 実行タイミング
- フィルター:ServletContainerレベルで最初に実行
- インターセプター:Spring MVCのDispatcherServlet後に実行
2. 取得できる情報
- フィルター:ServletRequest/ServletResponseのみ
- インターセプター:HandlerMethod(コントローラーのメソッド情報)も取得可能
3. 設定の柔軟性
- フィルター:URLパターンベースの設定
- インターセプター:より細かいパスパターン指定が可能
このような特徴を理解した上で、実装する機能に応じて適切な方を選択することが重要です。
ですって。頑張って理解しよ。
まとめ
今回は、interceptorについて整理してみました。何だか filterにも似ていますね。
用途の違いは重要になりそうですね。
フレームワークのパワーをひしひしと感じます。
皆様にとって参考になれば幸いです。
また何か書きます。