フィルター(Filter)とは?
フィルターは、J2EE標準仕様の機能であり、ディスパッチャサーブレット(Dispatcher Servlet)にリクエストが渡される前後に、URLパターンに一致するすべてのリクエストに対して追加の処理を行う機能を提供します。つまり、Springコンテナではなく、TomcatなどのWebコンテナによって管理されるため、ディスパッチャサーブレットに到達する前にリクエストを処理するのです。
フィルター(Filter)の実装
フィルターを追加するには、javax.servletのFilterインターフェースを実装する必要があります。このインターフェースには、以下の3つのメソッドがあります。
- initメソッド
- フィルターオブジェクトを初期化し、サービスに追加するためのメソッドです。Webコンテナはinitメソッドを1回呼び出してフィルターオブジェクトを初期化し、その後、リクエストはdoFilter()メソッドで処理されます。
- doFilterメソッド
- URLパターンに一致するすべてのHTTPリクエストがディスパッチャサーブレットに渡される前に、Webコンテナによって実行され、ディスパッチャサーブレットからクライアントにHTTPレスポンスが返される前に、Webコンテナによって実行されるメソッドです。
doFilter()の引数にはFilterChainがありますが、FilterChainのdoFilter()を使用して、次のターゲットにリクエストを渡します。chain.doFilter()の前後に必要な処理を追加することで、目的の処理を実行できます。
- URLパターンに一致するすべてのHTTPリクエストがディスパッチャサーブレットに渡される前に、Webコンテナによって実行され、ディスパッチャサーブレットからクライアントにHTTPレスポンスが返される前に、Webコンテナによって実行されるメソッドです。
- destoryメソッド
- フィルターオブジェクトをサービスから削除し、使用しているリソースを解放するためのメソッドです。これはWebコンテナによって1回呼び出され、その後はdoFilter()によって処理されなくなります。
サンプルコード - Servlet仕様
サンプルコード - @Component
- @Component: FilterをSpring Beanとして登録できます。
- @Order: フィルターが複数ある場合、順番を設定できます。
- サーブレット仕様のフィルターをこのようにSpring Beanとして登録すると、Spring仕様の他のBeanを使用できます。
サンプルコード - @Configuration
特定のURIでのみフィルターを動作させる場合は、FilterRegistrationBeanを使用してフィルターをSpring Beanとして登録できます。
フィルター(Filter)の用途
主にリクエストパラメータ自体の検証と処理を担当します。
- セキュリティ関連の共通処理
- フィルターはWebコンテナで動作するため、セキュリティチェック(XSS、CSRF対策など)を行い、不正なリクエストの場合はブロックできます。Springコンテナにリクエストが渡される前にブロックされるため、より高い安定性を確保できます。
- すべてのリクエストのロギング
- 画像/データの圧縮と文字列エンコーディング
- フィルターは、画像やデータの圧縮、文字列エンコーディングなど、Webアプリケーション全体で使用する機能を実装しました。
- ServletRequestのカスタマイズ
- HttpServletRequestは、Bodyの内容を1回のみ読み取ることができます。そのため、FilterまたはInterceptorではBodyを読み取ることができません。Bodyをログに記録するために、カスタムServletRequestを作成できます。
インターセプター(Interceptor)
インターセプター(Interceptor)とは?
インターセプター(Interceptor)は、J2EE標準仕様のフィルター(Filter)とは異なり、Springが提供する技術であり、ディスパッチャサーブレットがコントローラーを呼び出す前後に、リクエストとレスポンスを参照または加工できる機能を提供します。つまり、Webコンテナで動作するフィルターとは異なり、インターセプターはSpringコンテキストで動作するのです。
ディスパッチャサーブレットは、ハンドラーマッピングを使用して適切なコントローラーを検索してリクエストしますが、その結果、実行チェーン(HandlerExecutionChain)を返します。そのため、この実行チェーンには、1つ以上のインターセプターが登録されている場合、順番にインターセプターを通過してコントローラーが実行され、インターセプターがない場合は、コントローラーが直接実行されます。
インターセプター(Interceptor)の実装
インターセプターを追加するには、org.springframework.web.servlet.HandlerInterceptorインターフェースを実装する必要があります。このインターフェースには、以下の3つのメソッドがあります。
- preHandleメソッド
- preHandleメソッドは、コントローラーが呼び出される前に実行されます。そのため、コントローラーの前に処理する必要がある前処理や、リクエスト情報の加工や追加を行う場合に使用できます。
- preHandleメソッドの3番目の引数であるhandler引数は、@RequestMappingが付けられたメソッドの情報を抽象化したオブジェクトです。
- preHandleメソッドの戻り値はboolean型で、戻り値がtrueの場合は次のステップに進みますが、falseの場合は処理を中断し、以降の処理(次のインターセプターまたはコントローラー)は実行されません。
- postHandleメソッド
- postHandleメソッドは、コントローラーが呼び出された後に実行されます。そのため、コントローラーの後に処理する必要がある後処理がある場合に使用できます。
- afterCompletionメソッド
- afterCompletionメソッドは、その名の通り、すべてのビューで最終的な結果を作成する作業を含む、すべての処理が完了した後に実行されます。
サンプルコード
- 作成したインターセプターをBeanとして登録します。
- WebMvcConfigurerインターフェースのaddInterceptors()メソッドに作成したインターセプターを登録します。
- インターセプターは、InterceptorRegistryに登録した順番に動作します。
インターセプター(Interceptor)の用途
主にサービスロジックを利用して、リクエスト情報の整合性処理を担当します。
- 認証/認可などの共通処理
- 代表的なものとして、認証や認可など、クライアントのリクエストに関連する処理を、コントローラーに渡す前にチェックできます。
- API呼び出しのロギング
- 渡されるHttpServletRequest、HttpServletResponseオブジェクトを使用して、クライアントの情報を記録できます。
- コントローラーに渡すデータの加工
- 渡されるHttpServletRequest、HttpServletResponseオブジェクトを加工して、コントローラーに渡すことができます。
- AOPの模倣
- preHandle()メソッドの3番目の引数であるHandlerMethodで、実行されるメソッドのシグネチャなどの追加情報を確認し、ロジックの実行可否を判断できます。
フィルター(Filter)とインターセプター(Interceptor)
- フィルターはWebコンテキストで実行され、インターセプターはSpringコンテキストで実行されるため、実行タイミングが異なります。
- フィルターはディスパッチャサーブレットの前後を処理でき、インターセプターはコントローラーの前後を処理できます。
- フィルターはSpringに関係なく、グローバルに処理する必要があるタスクを実行する場合、または入力として渡されたパラメータ自体を検証する場合、またはHttpServletRequestの代わりにServletRequestを使用する場合は使用するのが適しています。
- インターセプターは、Springの範囲内でクライアントのリクエストに関連してグローバルに処理する必要がある場合、またはサービスロジックを組み合わせる必要がある場合に使用するのが適しています。
- インターセプターは、@ControllerAdviceと@ExceptionHandlerを使用して例外処理を行うことができますが、フィルターではこれらを使用して例外処理を行うことはできません。
- フィルターは、主にdoFilter()メソッドの周囲をtry〜catch文で囲んで、その時点で発生した例外をすぐに処理します。
Argument Resolver
Argument Resolverとは?
Argument Resolverは、コントローラーにリクエストが渡されると、リクエストに含まれる値から目的のオブジェクトを作成する作業を間接的に行うことができます。
Argument Resolverの用途
例えば、JWTトークンと共にリクエストが渡されたとします。このトークンが有効なトークンかどうかを検証し、トークンに保存されているIDを取り出して、ログインユーザーオブジェクトを作成する必要があります。
このような場合、Argument Resolverを使用しないと、トークンを検証してログインユーザーオブジェクトに変換する処理を、すべてのコントローラーに実装する必要があります。すると、ユーザー検証が必要なコントローラーに重複コードが発生し、コントローラーの責任が増加します。Argument Resolverはこのような問題を解決できます。
Argument Resolverの実装
ArgumentResolverは、HandlerMethodArgumentResolverを実装することで使用できます。そして、このインターフェースは、以下の2つのメソッドを実装するように指定しています。
- supportsParameterメソッド
- ArgumentResolverを実行したいParameterの前に、特定のアノテーションを作成して付けます。supportsParameter()メソッドは、リクエストされたメソッドの引数に目的のアノテーションが付いているかどうかを確認し、目的のアノテーションが含まれている場合はtrueを返します。
- resolveArgumentメソッド
- supportsParameterでtrueを受け取った場合、つまり、特定のアノテーションが付いているメソッドがある場合、parameterを目的の形式で情報バインディングして返すメソッドです。
このようにArgumentResolverを使用した場合、コントローラーの実装は次のようになります。
Argument Resolverとインターセプター(Interceptor)の違い
- ArgumentResolverはインターセプターの後で動作し、コントローラーにリクエストが渡されると、リクエストに含まれる値から目的のオブジェクトを返します。
- 一方、インターセプターは実際にコントローラーが実行される前にリクエストを傍受し、特定のオブジェクトを返すことはできません。booleanまたはvoidの戻り値型のみが存在します。
出典
- https://mangkyu.tistory.com/173
- https://junhyunny.github.io/spring-boot/filter-interceptor-and-aop/
- https://supawer0728.github.io/2018/04/04/spring-filter-interceptor/
- https://tecoble.techcourse.co.kr/post/2021-05-24-spring-interceptor/
- https://baeldung-cn.com/spring-mvc-handlerinterceptor-vs-filter
- https://www.baeldung.com/spring-boot-add-filter
想定される面接質問と回答
フィルターとは?
フィルターは、ディスパッチャサーブレットにリクエストが渡される前後に、URLパターンに一致するすべてのリクエストに対して追加の処理を行う機能を提供し、サーブレットコンテナで動作します。
フィルターの使用時期は?
フィルターでは、Springに関係なく、グローバルに処理する必要がある処理を行うことができます。または、リクエストパラメータを基に検証する場合に使用されます。
代表的には、セキュリティ関連の共通処理、すべてのリクエストのロギング、画像/データの圧縮と文字列エンコーディング、ServletRequestのカスタマイズ処理などを処理します。
インターセプターとは?
インターセプターは、ディスパッチャサーブレットがコントローラーを呼び出す前後に、リクエストとレスポンスを参照または加工できる機能を提供し、つまり、Springコンテナで動作します。
インターセプターの使用時期は?
インターセプターは、クライアントのリクエストに関連してグローバルに処理する必要がある処理を行うことができ、検証時にサービスロジックを呼び出す場合に主に使用されます。
代表的には、認証/認可などの共通処理、API呼び出しのロギング、コントローラーに渡すデータの加工処理などを処理します。
フィルターとインターセプターの違いは?
フィルターはサーブレットコンテナで実行され、インターセプターはSpringコンテナで実行されます。
フィルターはディスパッチャサーブレットの前後を処理でき、インターセプターはコントローラーの前後を処理できます。
したがって、フィルターはSpringに関係なく、グローバルに処理する必要がある処理を行うのに適しており、インターセプターはクライアントのリクエストに関連してグローバルに処理する必要がある処理を行うのに適しています。
Argument Resolverとは?
Argument Resolverは、コントローラーにリクエストが渡されると、リクエストに含まれる値から目的のオブジェクトを作成する作業を間接的に行います。
Argument Resolverの使用時期は?
JWTトークンと共にリクエストが渡されたとき、有効なトークンかどうかを検証し、トークンに保存されているIDを取り出してLoginMemberというオブジェクトを作成する処理を行う場合に使用できます。
インターセプターとArgument Resolverの違いは?
Argument Resolverは、コントローラーにリクエストが渡されると、リクエストに含まれる値から目的のオブジェクトを返します。一方、インターセプターは上記のようにオブジェクトを返すことはできず、インターセプターが実行された後にArgument Resolverが実行されます。
コメント0