言語を選択
durumis AIが要約した文章
- フィルタは、ディスパッチャサーブレットにリクエストが渡される前後に、URLパターンに合致するすべてのリクエストに対して追加処理を実行できる機能を提供する J2EE標準仕様機能であり、ウェブコンテナで動作します。 一方、インターセプターはSpringが提供する技術であり、ディスパッチャサーブレットがコントローラを呼び出す前後に、 リクエストとレスポンスを参照したり加工したりできる機能を提供し、Springコンテキストで動作します。
- Argument Resolverは、コントローラにリクエストが来たときに、リクエストに含まれる値から目的のオブジェクトを作成する役割を間接的に担うことができます。
- フィルタとインターセプターは、Springコンテナで動作するかウェブコンテナで動作するか、 ディスパッチャサーブレットを基準にリクエストを処理するかコントローラを基準にリクエストを処理するかによって違いがあり、 Argument Resolverはインターセプターの後で動作し、特定のオブジェクトを返します。
フィルター (Filter) とは?
フィルターは、J2EE標準仕様機能で、ディスパッチャーサーブレット (Dispatcher Servlet) にリクエストが渡される前後に、 URLパターンに一致するすべてのリクエストに対して、追加の処理を実行できる機能を提供します。つまり、スプリングコンテナではなく、 TomcatなどのWebコンテナによって管理されるため、ディスパッチャーサーブレットに到達する前にリクエストを処理するのです。
フィルター (Filter) の実装
フィルターを追加するには、javax.servlet の Filter インターフェースを実装する必要があります。このインターフェースには、 以下の3つのメソッドがあります。
- init メソッド
- フィルターオブジェクトを初期化し、サービスに追加するためのメソッドです。Webコンテナは、1回 init メソッドを呼び出して フィルターオブジェクトを初期化し、その後、リクエストは doFilter() メソッドによって処理されます。
- doFilter メソッド
- URLパターンに一致するすべてのHTTPリクエストがディスパッチャーサーブレットに渡される前に、Webコンテナによって実行され、 ディスパッチャーサーブレットからクライアントにHTTP応答が行く前に、Webコンテナによって実行されるメソッドです。
doFilter() のパラメータには FilterChain があります。FilterChain の doFilter() を使用して、次のターゲットにリクエストを渡します。chain.doFilter() の前後に必要な処理を追加することで、 目的の処理を実行できます。
- URLパターンに一致するすべてのHTTPリクエストがディスパッチャーサーブレットに渡される前に、Webコンテナによって実行され、 ディスパッチャーサーブレットからクライアントにHTTP応答が行く前に、Webコンテナによって実行されるメソッドです。
- destory メソッド
- フィルターオブジェクトをサービスから削除し、使用しているリソースを返却するためのメソッドです。これは、Webコンテナによって1回 呼び出され、その後は doFilter() によって処理されなくなります。
サンプルコード - Servlet 仕様
@WebFilter("/*")
public class dolphagoFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
//ここに前処理
request.setCharacterEncoding("UTF-8");
System.out.println("doFilter() 前....");
filterChain.doFilter(request, response);
//ここに後処理
System.out.println("doFilter() 後....");
}
@Override
public void destroy() { }
サンプルコード - @Component
@Order(1)
@Component
public class CustomFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException { }
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
chain.doFilter(request, response);
}
@Override
public void destroy() { }
- @Component: フィルターをスプリングBeanとして登録できます。
- @Order: フィルターが複数ある場合、順番を指定できます。
- サーブレット仕様に準拠したフィルターをこのようにスプリングBeanとして登録すると、スプリング仕様に準拠した他のBeanを使用できます。
サンプルコード - @Configuration
@Configuration
public class CustomRegistrationBean {
@Bean
public FilterRegistrationBean customFilterBean() {
FilterRegistrationBean registration = new FilterRegistrationBean<>();
registration.setFilter(new CustomFilter());
registration.setOrder(1);
registration.addUrlPatterns("/api/*");
return registration;
}
特定のURIにのみフィルターを動作させたい場合は、FilterRegistrationBeanを使用して、フィルターをスプリングBeanとして登録できます。
フィルター (Filter) の用途
主に、リクエストパラメータ自体の検証と処理を担当します。
- セキュリティ関連の共通作業
- フィルターは、Webコンテナで動作するため、セキュリティチェック (XSS、CSRF対策など) を実行し、 不正なリクエストの場合にはブロックできます。スプリングコンテナにリクエストが渡される前にブロックされるため、 セキュリティをさらに強化できます。
- すべてのリクエストに対するロギング
- 画像/データの圧縮と文字列エンコーディング
- フィルターは、画像やデータの圧縮、文字列エンコーディングなど、Webアプリケーション全体で使用する機能を実装しました。
- ServletRequest のカスタマイズ
- HttpServletRequest は、Body の内容を1回だけ読み取ることができます。そのため、Filter や Interceptor では、Body を読み取ることができません。Body をロギングするために、カスタムした ServletRequest を作成できます。
インターセプター (Interceptor)
インターセプター (Interceptor) とは?
インターセプター (Interceptor) は、J2EE標準仕様のフィルター (Filter) とは異なり、スプリングが提供する技術です。 ディスパッチャーサーブレットがコントローラーを呼び出す前後に、リクエストとレスポンスを参照したり加工したりできる機能を提供します。 つまり、Webコンテナで動作するフィルターとは異なり、インターセプターはスプリングコンテキストで動作します。
ディスパッチャーサーブレットは、ハンドラーマッピングを通じて適切なコントローラーを見つけ出すようにリクエストしますが、 その結果として、実行チェーン (HandlerExecutionChain) を返します。そのため、この実行チェーンには、1つ以上のインターセプターが登録されている場合、 順次インターセプターを通過してからコントローラーが実行されるようになり、インターセプターがない場合は、すぐにコントローラーが実行されます。
インターセプター (Interceptor) の実装
インターセプターを追加するには、org.springframework.web.servlet.HandlerInterceptor インターフェースを実装する必要があります。このインターフェースには、以下の3つのメソッドがあります。
- preHandle メソッド
- preHandle メソッドは、コントローラーが呼び出される前に実行されます。そのため、コントローラーの前に処理する必要がある 前処理作業や、リクエスト情報を加工したり追加したりする場合に使用できます。
- preHandle メソッドの3番目のパラメータである handler パラメータは、@RequestMapping が付いた メソッドの情報を抽象化したオブジェクトです。
- preHandle メソッドの戻り値の型は boolean です。戻り値が true の場合、次の段階に進みますが、 false の場合は処理を中止し、以降の処理 (次のインターセプターまたはコントローラー) は実行されません。
- postHandle メソッド
- postHandle メソッドは、コントローラーが呼び出された後に実行されます。そのため、コントローラーの後に処理する必要がある 後処理作業がある場合に使用できます。
- afterCompletion メソッド
- afterCompletion メソッドは、名前のとおり、すべてのビューで最終的な結果が生成されるのを含め、すべての処理が 完了した後に実行されます。
サンプルコード
@Component
public class CustomInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
}
@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomInterceptor());
registry.addInterceptor(new LoggingInterceptor());
}
- 作成したインターセプターをBeanとして登録します。
- WebMvcConfigurer インターフェースの addInterceptors() メソッドに、作成したインターセプターを登録します。
- インターセプターは、InterceptorRegistry に登録した順に動作します。
インターセプター (Interceptor) の用途
主に、サービスロジックを活用して、リクエスト情報の整合性処理を担当します。
- 認証/認可などの共通作業
- 代表的な例として、認証や認可など、クライアントのリクエストに関連する作業を、コントローラーに渡す前に検査できます。
- API呼び出しに関するロギング
- 渡される HttpServletRequest、HttpServletResponse オブジェクトを通じて、クライアントの 情報を記録できます。
- コントローラーに渡すデータの加工
- 渡される HttpServletRequest、HttpServletResponse オブジェクトを加工して、コントローラーに 渡すことができます。
- AOP の模倣
- preHandle() メソッドの3番目のパラメータである HandlerMethod によって、実行されるメソッドのシグネチャなど、 追加情報を把握して、ロジックの実行の可否を判断できます。
フィルター (Filter) vs インターセプター (Interceptor)
- フィルターはWebコンテキストで実行され、インターセプターはスプリングコンテキストで実行されるため、実行タイミングが異なります。
- フィルターは、ディスパッチャーサーブレットの前後を処理できますが、インターセプターは、コントローラーの前後を処理できます。
- フィルターは、スプリングに関係なく、グローバルに処理する必要がある作業を実行する場合や、入力として渡されたパラメータ自体を検証する場合や、 HttpServletRequest の代わりに ServletRequest を使用する場合は、フィルターを使用するのが適しています。
- インターセプターは、スプリングに到達したクライアントのリクエストに関連して、グローバルに処理する必要がある場合や、サービスロジックを混在させる必要がある場合は、 インターセプターを使用するのが適しています。
- インターセプターは、@ControllerAdvice と @ExceptionHandler を使用して、例外処理を行うことができますが、フィルターは、 これらを使用して例外処理を行うことはできません。
- フィルターは、主に doFilter() メソッドの周囲を try~catch 文で囲むことで、その時点で発生した例外を すぐに処理します。
Argument Resolver
Argument Resolver とは?
Argument Resolver は、あるリクエストがコントローラーに渡されたときに、リクエストに含まれる値から、目的のオブジェクトを作成する作業を、 間接的に行うことができます。
Argument Resolver の用途
たとえば、JWTトークンとともにリクエストが渡されたとします。このトークンが有効なトークンであることを検証し、トークンに保存されている id を取得して、ログインユーザーオブジェクトを作成する必要があります。
このような場合、Argument Resolver を使用しないと、トークンを検証してログインユーザーオブジェクトに変換する処理を、すべてのコントローラーで 実装する必要があります。すると、ユーザーの検証が必要なコントローラーに重複コードが発生し、コントローラーの責任が増加します。このような 問題を Argument Resolver が解決できます。
Argument Resolver の実装
ArgumentResolver は、HandlerMethodArgumentResolver を実装することで使用できます。このインターフェースは、 以下の2つのメソッドを実装するように指定しています。
boolean supportsParameter(MethodParameter parameter);
@Nullable
- supportsParameter メソッド
- ArgumentResolver を実行させたい Parameter の前に、特定の注釈を作成して貼り付けます。 supportsParameter() メソッドは、リクエストされたメソッドの引数に目的の注釈が貼り付けられているかどうかを確認し、 目的の注釈が含まれている場合は、true を返します。
- resolveArgument メソッド
- supportsParameter で true を受け取った場合、つまり、特定の注釈が貼り付けられているメソッドがある場合、 parameter が目的の形式で情報をバインドして返します。
このように ArgumentResolver を使用すると、コントローラーの実装は次のようになります。
@GetMapping("/me")
public ResponseEntity findMemberOfMine(@AuthenticationPrincipal LoginMember loginMember) {
MemberResponse memberResponse = memberService.findMember(loginMember.getId());
return ResponseEntity.ok().body(memberResponse);
Argument Resolver とインターセプター (Interceptor) の違い
- Argument Resolver は、インターセプターの後で動作し、あるリクエストがコントローラーに渡されたときに、リクエストに含まれる 値から目的のオブジェクトを返します。
- 一方、インターセプターは、実際にコントローラーが実行される前にリクエストを傍受し、特定のオブジェクトを返却することはできません。 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パターンに一致するすべてのリクエストに対して、 追加の処理を実行できる機能を提供し、サーブレットコンテナレベルで動作します。
フィルターを使用するタイミングは?
フィルターでは、スプリングに関係なく、グローバルに処理する必要がある作業を処理できます。または、リクエストパラメータを中心に検証する場合に 使用されます。
代表的な例としては、セキュリティ関連の共通作業、すべてのリクエストに対するロギング、画像/データの圧縮と文字列エンコーディング、 ServletRequest のカスタマイズ処理があります。
インターセプターとは?
インターセプターは、ディスパッチャーサーブレットがコントローラーを呼び出す前後に、リクエストとレスポンスを参照したり加工したりできる機能を提供し、 つまり、スプリングコンテナレベルで動作します。
インターセプターを使用するタイミングは?
インターセプターは、クライアントのリクエストに関連して、グローバルに処理する必要がある作業を処理でき、検証時にサービスロジックを呼び出す場合に 主に使用されます。
代表的な例としては、認証/認可などの共通作業、API呼び出しに関するロギング、コントローラーに渡すデータの加工処理があります。
フィルターとインターセプターの違いは?
フィルターはサーブレットコンテナレベルで実行され、インターセプターはスプリングコンテナレベルで実行されます。
フィルターは、ディスパッチャーサーブレットの前後を処理できますが、インターセプターは、コントローラーの前後を処理できます。
したがって、フィルターは、スプリングに関係なく、グローバルに処理する必要がある作業を実行するのに適しており、インターセプターは、 クライアントのリクエストに関連して、グローバルに処理する必要がある作業を実行するのに適しています。
Argument Resolver とは?
Argument Resolver は、あるリクエストがコントローラーに渡されたときに、リクエストに含まれる値から、目的のオブジェクトを作成する作業を、 間接的に行います。
Argument Resolver を使用するタイミングは?
JWTトークンとともにリクエストが渡されたときに、有効なトークンであることを検証し、トークンに保存されている id を取得して、 LoginMember というオブジェクトを作成する作業を行う場合に使用できます。
インターセプターと Argument Resolver の違いは?
Argument Resolver は、あるリクエストがコントローラーに渡されたときに、リクエストに含まれる値から目的のオブジェクトを返します。 一方、インターセプターは、このようにオブジェクトを返却することはできず、インターセプターが実行された後に Argument Resolver が実行されます。