【ITニュース解説】Filters vs Interceptors vs AOP — when each
2026年09月19日に「Dev.to」が公開したITニュース「Filters vs Interceptors vs AOP — when each」について初心者にもわかりやすく解説しています。
ITニュース概要
Webアプリの共通処理を担うFilter, Interceptor, AOPは、機能する層が異なる。FilterはHTTP全体、InterceptorはSpring MVC内のコントローラー、AOPはあらゆるメソッドで動作し、見える情報が違う。目的に応じた適切な使い分けが重要だ。
ITニュース解説
Webアプリケーション開発において、特定の処理を複数の箇所で共通して実行したい場面は多々ある。例えば、すべてのリクエストに対するログの記録、ユーザー認証、処理時間の計測、HTTPヘッダーの追加、パフォーマンス指標の収集などがこれにあたる。このような処理は、アプリケーションの「本来の仕事」ではないものの、多くのリクエストやメソッドに関わるため、「横断的関心事」と呼ばれている。これらの処理を個々のコントローラメソッドに毎回記述すると、コードが重複し、管理が複雑になり、記述漏れも発生しやすくなる。Springフレームワークでは、このような横断的関心事を効率的に一元管理するために、Filter、HandlerInterceptor、AOP (Aspect-Oriented Programming)という三つの強力なツールを提供している。これらのツールは、Webリクエストがアプリケーションの内部に進む「道のり」の中で、それぞれ異なる位置で動作し、異なる情報にアクセスできるという違いがある。
Webリクエストがサーバーに届き、アプリケーションによって処理されるまでの道のりを理解することは、これらのツールの違いを把握する上で非常に重要だ。まず、ユーザーからのHTTPリクエストは、TomcatやJettyのような「Servletコンテナ」と呼ばれるWebサーバーによって受け取られる。Servletコンテナは、HTTP通信を担当し、生のリクエスト(HttpServletRequest)をアプリケーションに渡す。次に、Springアプリケーションの「正面玄関」として機能する「DispatcherServlet」がリクエストを受け取る。DispatcherServletは、URLに基づいてどのコントローラメソッドがこのリクエストを処理すべきかを判断し、そのメソッドを呼び出し、戻り値をHTTPレスポンスに変換する役割を担う。最後に、選ばれた「Handler」、つまり特定のコントローラメソッドが実際のビジネスロジックを実行する。
これらの三つのツールは、このリクエストの道のりの中で、外側から内側へと順に配置されている。最も外側に位置するのがServlet Filter、その内側でDispatcherServletが処理を行う際に動作するのがHandlerInterceptor、そして最も深い層、つまりコントローラメソッドやビジネスロジックを担う任意のBeanメソッドの周りで動作するのがAOP (Aspect)である。内側に向かうにつれて、ツールは生のリクエストに関する情報よりも、Springフレームワークの内部的な詳細やビジネスロジックに関する情報をより多く見られるようになる。
まず、最も外側の層であるServlet Filterについて説明する。これはJava EEのServlet仕様の一部であり、Springフレームワークのコードが実行されるよりも前の段階で動作する。Filterは生のリクエスト(HttpServletRequest)とレスポンス(HttpServletResponse)にアクセスできるが、Springがどのコントローラメソッドを呼び出すかは知らない。例えば、すべてのHTTPリクエストに対して処理時間の計測を行いたい場合、Filterを利用できる。FilterのdoFilterメソッド内で、処理開始時刻を記録し、chain.doFilter(...)を呼び出すことで次のFilterやアプリケーション本体へ処理を渡す。その後、chain.doFilter(...)からの戻り時に終了時刻を記録すれば、全体のリクエスト処理時間を計測できる。もしchain.doFilter(...)を呼び出さなければ、そのFilterでリクエストの処理を完全に停止させることも可能だ。これは、APIキーの検証など、Springが起動する前に不正なリクエストをブロックするセキュリティゲートのような役割に適している。Filterは、CORS(オリジン間リソース共有)ヘッダーの追加、リクエストボディの圧縮、キャッシュ制御など、すべてのHTTPリクエストに共通して適用される低レベルな処理や、Springが処理を開始する前に実行する必要があるセキュリティ関連の処理に役立つ。
次に、Spring MVCの内部で動作するHandlerInterceptorを見てみよう。InterceptorはDispatcherServletの内部で動作するため、Filterとは異なり、どのハンドラ(コントローラメソッド)がリクエストを処理する予定であるかを知っている。この知識があるため、特定のコントローラメソッドやURLパターンに対して処理を適用できる。Interceptorには、コントローラメソッドの呼び出し前に実行されるpreHandle、コントローラメソッドの実行後、かつビューがレンダリングされる前に実行されるpostHandle、そしてすべての処理が完了した後、エラーが発生した場合でも実行されるafterCompletionという三つのフック(特定の処理を行うための場所)が用意されている。例えば、特定のAPIパスへのリクエストに対してユーザーが認証済みであるかをpreHandleでチェックし、認証されていない場合はfalseを返してコントローラメソッドの実行を停止させることができる。Interceptorは、特定のWebエンドポイントごとの認証チェック、モデルの調整、コントローラメソッド名を伴うリクエストのロギングなど、Spring MVCのライフサイクルに関連し、かつハンドラの情報が必要なWeb特有の横断的関心事に適している。
最後に、最も深い層で、あらゆるSpring Beanメソッドに対して動作するのがAOP (Aspect-Oriented Programming)である。これまでの二つのツールがWebリクエストのみを対象としていたのに対し、AOPはWebリクエストだけでなく、スケジューリングされたタスクやメッセージリスナーからの呼び出しなど、あらゆる場所からのSpring Beanメソッドの呼び出しに対して処理を実行できる。AOPは、SpringがBeanの代わりに「プロキシ」と呼ばれる代理オブジェクトを作成することで機能する。このプロキシは、見た目は本来のBeanと同じだが、実際のメソッド呼び出しの前後に特別なコード(「Advice」と呼ばれる)を実行する。どのメソッドにAdviceを適用するかは、「Pointcut」と呼ばれるルールで指定する。例えば、サービス層のすべてのメソッドの実行時間を計測したい場合、AOPを利用できる。@Aroundアノテーションを使って、指定したPointcutに合致するメソッドの呼び出しをラップするAdviceを記述する。このAdviceの中でProceedingJoinPoint.proceed()を呼び出すことで、本来のメソッドが実行される。AOPは、トランザクション管理(@Transactional)、キャッシング(@Cacheable)、メソッドレベルのセキュリティ、カスタムの監査ロギングなど、Webの概念とは直接関係なく、ビジネスロジックのレベルで横断的に適用される機能の実装に非常に強力なツールとなる。
AOPを利用する上で一つ注意すべき点として、「プロキシの自己呼び出し問題」がある。これは、同じBeanの内部から別のメソッドを呼び出す場合(例えばthis.otherMethod())、プロキシが介在せず、本来のBeanが直接呼び出されてしまうため、そのメソッドに設定されたAOPのAdviceが実行されないという現象だ。これは、トランザクションが適用されないなどの予期せぬ動作を引き起こすことがあるため、特に初心者は注意が必要である。
これらのツールを適切に選択することが重要だ。例えば、特定のWebエンドポイントに対する認証をFilterで実装しようとすると、Filterはどのコントローラが呼ばれるか知らないため、URLを自分で解析して判断するしかなく、Springのルーティングと重複し、脆い実装になりがちだ。この場合、ハンドラの情報を知るInterceptorや、メソッドレベルのセキュリティ機能を利用する方が適切である。逆に、HTTPレスポンスヘッダーの設定をAOPで試みても、サービスメソッドは生のリクエストやレスポンスに直接アクセスできないため、不適切となる。これはFilterやInterceptorを使うべき場面である。
まとめると、FilterはServletレベルで最も外側に位置し、生のリクエスト/レスポンスにアクセスできるが、コントローラ情報は知らない。すべてのHTTPリクエストに対して、Springが起動する前の処理に適している。InterceptorはSpring MVCの内部に位置し、ハンドラの情報も知っており、Webリクエストのライフサイクルに沿った処理に適している。そしてAOPは最も深い層で、あらゆるSpring Beanメソッドに対して動作し、Webとは無関係にビジネスロジックレベルの横断的関心事を処理するのに適している。それぞれのツールにおける「次の処理に進む」ための呼び出しは、Filterではchain.doFilter()、InterceptorではpreHandleのreturn true、AOPではpjp.proceed()と異なるが、いずれも処理の流れを継続させるための重要なステップだ。
ツールを選ぶ際の決定ルールとしては、「すべてのHTTPリクエスト、特にエラーや静的ファイルに対しても実行したいか、あるいはSpringが起動する前に実行したいか」という場合はFilterを選ぶ。「どのコントローラ/エンドポイントが実行されるか知る必要があり、Web MVCのライフサイクルにフックしたいか」という場合はInterceptorを選ぶ。そして「サービスやビジネスロジックのメソッドの周りで実行したい、あるいはWeb以外の呼び出しに対しても実行したいか」という場合はAOPを選ぶと良いだろう。基本的には、HTTP関連の低レベルな処理は外側の層、ビジネスロジックに近い処理は内側の層と考えると理解しやすい。必要な情報が手に入る最も外側の層を選ぶのが良い実践方法である。