NHN Cloud Meetup 編集部
(Spring)FilterとInterceptorの違い
2018.05.21
23,159
はじめに
今回は、Spring Web Applicationで使用するFilterとInterceptorについて、紹介したいと思います。
実行時
出典:https://justforchangesake.wordpress.com/2014/05/07/spring-mvc-request-life-cycle/ |
一般的な内容
- FilterとInterceptorは、実行される時点が異なる。
- FilterはWeb Applicationに登録して、InterceptorはSpringのContextに登録する。
追加で紹介したい内容
Tomcatの場合、deployment descriptor(/WEB-INF/web.xml)に使用するFilterを登録します。そのため、アプリケーション全体に影響を与える作業はFilterにする、という意見があります。しかし実際はそうではありません。FilterもInterceptorもすべての要求に対する前後処理の役割を遂行します。またuriベースで、いつ実行するか調整可能で、直接requestの内容を把握し、希望する条件を満たせばロジックを実行できるという点で差がありません。
実行時点が異なるため、最も大きな影響を受けるものは、例外処理(Exception Handling)です。Filterで例外が発生すると、Web Applicationで処理する必要があります。Tomcatを使用する場合、<error-page>をうまく宣言するか、Filter内で例外をキャッチし、request.getRequestDispatcher(String)に例外処理を送る必要があります。しかしInterceptorで例外が発生したらどうなるでしょうか?Interceptorの実行時点を見てみると、SpringのServletDispatcher内にあります。つまり@ControllerAdviceで@ExceptionHandlerを使って例外処理ができます。Spring Web Applicationの例外処理方法については、参考記事をご覧ください。例外処理が強固なApplicationはメンテナンスがしやすいでしょう。前後処理ロジックで例外を全域に処理したいならInterceptorを使ってみよう。
Interface
当然ながらinterfaceが違います。実行メソッドを比較してみよう。
Filter
public interface Filter { void doFilter(ServletRequest request, ServletResponse response, FilterChain chain); }
HandlerInterceptor
public interface HandlerInterceptor { boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler); void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mav); void afterCompletion(HttpServletRequest request, HttpServeletResponse response, Object handler, Exception ex); }
一般的な内容
- FilterはServletで処理の前後を扱うことができる。
- InterceptorはHandlerの実行前(preHandle)、Handler実行後(postHandle)、viewレンダリング後(afterCompletion)など、Servlet内でもメソッドに基づいて実行時点が異なる。
追加で紹介したい内容
Interceptorでのみできること
- AOPの模倣ができる。@RequestMapping宣言で要求に対するHandlerMethod(@Controllerのメソッド)が決まったら、handlerという名前でHandlerMethod
が入る。HandlerMethodでメソッドの署名など、追加の情報を把握して、ロジックを実行するか判断することができる。
- Viewをレンダリングする前に追加作業ができる。例えばWebページでGNB(Global Navigation Bar)を、権限によって異なる項目で露出させる等の処理ができる。
Filterでのみできること
ServletRequestまたはServletResponseを交換できます。以下のようなことが可能です。
public class SomeFilter implements Filter { //... public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { chain.doFilter(new CustomServletRequest(), new CustomResponse()); } }
HttpServletRequestのbody(ServletInputStreamの内容)をLoggingを例に挙げてみよう。HttpServletRequestはbodyの内容を一度だけ読むことができます。Rest API Applicationを作成する際、一般的にjson形式で要求を受けとります。@Controller(Handler)にリクエストが入ってきてbodyを一度読むことになります。そのため、FilterやInterceptorではbodyを読み取ることができません。IOExceptionが発生します。bodyをLoggingするにはHttpServletRequestの代わりに何度もinputStreamを開けるようにカスタマイズされたServletRequestを使うしかありません。
public class SomeFilter implements Filter { //... public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { chain.doFilter(new BodyCachedServletRequestWrapper(request), response); } }
まとめ
SpringでWeb Applicationを作成するとき、一度ぐらいはFilterとInterceptorを選択しなければならない状況に置かれるでしょう。前後処理という点で、その役割は似ていますが、実際に実行される時点でできることが異なりますので注意しましょう。