HandlerInterceptor
が呼ばれる流れを追ってみる。
ソースコードのバージョンはSpring Boot 2.3.0.RELEASE
。
HandlerInterceptor
は以下のようにpreHandle
, postHandle
, afterComplition
を実装することで、Controllerのメソッド(ハンドラ)の前後に任意の処理を実行させることができる。
public class FooInterceptor extends HandlerInterceptorAdapter { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("`preHandle`"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("`postHandle`"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("`afterCompletion`"); } }
今回は、preHandle
, postHandle
, afterCompletion
がいつどのように実行されているのかをコードを読んで追っていく。
まずは各メソッドを呼び出している箇所を読んでみる。
各メソッドはそれぞれ、HandlerExecutionChainクラスのapplyPreHandle
, applyPostHandle
, triggerAfterCompletion
というメソッド内で呼ばれている。
public class HandlerExecutionChain { /* 中略 */ boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = 0; i < interceptors.length; i++) { HandlerInterceptor interceptor = interceptors[i]; if (!interceptor.preHandle(request, response, this.handler)) { triggerAfterCompletion(request, response, null); return false; } this.interceptorIndex = i; } } return true; } void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = interceptors.length - 1; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; interceptor.postHandle(request, response, this.handler, mv); } } } void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) throws Exception { HandlerInterceptor[] interceptors = getInterceptors(); if (!ObjectUtils.isEmpty(interceptors)) { for (int i = this.interceptorIndex; i >= 0; i--) { HandlerInterceptor interceptor = interceptors[i]; try { interceptor.afterCompletion(request, response, this.handler, ex); } catch (Throwable ex2) { logger.error("HandlerInterceptor.`afterCompletion` threw exception", ex2); } } } } /* 中略 */ }
applyPreHandle
では、定義されているinterceptorのpreHandle
メソッドを順に呼び出していき、
全てがtrueを返す場合(interceptorのpreHandle
を実行して問題がなかった場合)は後続の処理を進めている。
preHandle
を実行した結果、falseを返すinterceptorがあった場合は、
afterCompletion
を実行するメソッドtriggerAfterCompletion
を呼び出した後、リクエストを失敗させる。
applyPostHandle
では、定義されているinterceptorのpostHandle
メソッドを順に呼び出していき、その後後続の処理を進めていく。
triggerAfterCompletion
では、定義されているinterceptorのafterCompletion
メソッドを順に呼び出していく。
このとき実行されるinterceptorはpreHandle
でtrueを返したinterceptorだけになるので注意が必要。
次にapplyPreHandle
, applyPostHandle
, triggerAfterCompletion
メソッドを呼び出している箇所を読んでいく。
applyPreHandle
, applyPostHandle
, triggerAfterCompletion
メソッドはDispatcherServletのdoDispatchメソッドで呼ばれている。
このメソッドは、リクエストに対応するハンドラ(Controllerのメソッド)を探し、処理の委譲を行う。
public class DispatcherServlet extends FrameworkServlet { /* 中略 */ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { /* 中略 */ mappedHandler = getHandler(processedRequest); /* 中略 */ if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); /* 中略 */ mappedHandler.applyPostHandle(processedRequest, response, mv); /* 中略 */ processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException); /* 中略 */ } /* 中略 */ private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, @Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv, @Nullable Exception exception) throws Exception { /* 中略 */ if (mv != null && !mv.wasCleared()) { render(mv, request, response); if (errorView) { WebUtils.clearErrorRequestAttributes(request); } } /* 中略 */ if (mappedHandler != null) { mappedHandler.triggerAfterCompletion(request, response, null); } } }
dispatcherServlet.doDispatch
メソッドの大まかな処理の流れは、以下のような流れになっている。
getHandler
メソッドでHandlerExecutionChain
を取得各interceptorの
preHandle
メソッドを実行リクエストに対応するハンドラを実行
各interceptorの
postHandle
メソッドを実行各interceptorの
afterCompletion
を実行
コードを読んでみて、afterCompletion
はレンダリング処理の後に実行される、とかDispatcherServlet.doDispatch
メソッドを読めばHandlerInterceptor
の実行処理の流れがわかる、とかHandlerExecutionChain
がinterceptorのリストを持っている、など色々学びがあった。
参考資料
spring-framework/HandlerInterceptor.java at master · spring-projects/spring-framework · GitHub
spring-framework/HandlerExecutionChain.java at master · spring-projects/spring-framework · GitHub
spring-framework/DispatcherServlet.java at master · spring-projects/spring-framework · GitHub