Interceptorの実行順序ってどうなってるんだろうと思い、ソースコードを読んだ内容をまとめます。
Interceptorの基本
HanlderInterceptorには3つのメソッドがあります。
それらが実行されるタイミングは以下の通りです。
メソッド名 | 実行タイミング |
---|---|
preHandle | handlerが実行される前に呼び出されます。 |
postHandle | handlerの実行が正常に完了した後に呼び出されます。handlerで処理されない例外が発生した場合は実行されません。 |
afterCompletion | Viewによるrender処理が完了した後に呼び出されます。handlerやViewのrender処理で例外が発生していても実行されます。 |
Interceptorが複数あるとどうなるのか
ここからが本題になってきます。
この辺の処理の流れを理解するには、DispatcherServlet.doDispatchメソッドを追いかけるといいです。
まず、HandlerExecutionChain.applyPreHandleメソッドでpreHandleを実行していることがわかります。
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;
}
上記を見てわかる通り、for文でループして実行しているだけですので、配列に格納されている順序で実行されることがわかります。
Interceptorはどういう順序で格納されるのかがわかれば、実行順序もわかります。
が、ここからはなかなか処理を追うのが困難でした。
XMLで<mvc:interceptors>
タグを用いている場合は、InterceptorsBeanDefinitionParserによって、Bean定義の処理が行われます。
ここでは、XMLを上から順に処理しているので、基本的にはInterceptorを定義した順に実行するのであろうと思われます。
preHandle
先ほどの通り、Bean定義された順に実行されます。
postHandle
HandlerExecutionChain.applyPostHandleメソッドから実行されます。
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, 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);
}
}
}
forループが逆順になっています。そのため、preHandleとは逆順で実行されます。
afterCompletion
HandlerExecutionChain.triggerAfterCompletionメソッドから実行されます。
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, 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);
}
}
}
}
これもforループが逆順になっています。そのため、preHandleとは逆順で実行されます。
preHandleがfalseを返却したらどうなるのか
preHandleはbooleanを返却します。falseを返却した場合は、handlerの実行が行われません。
では、Interceptorが複数ある場合はどうなるのでしょうか。
HandlerExecutionChain.applyPreHandleメソッドを見ればわかりますね。
(再掲)
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;
}
preHandleがfalseを返却した段階で、triggerAfterCompletionに処理が移っています。
また、preHandleがtrueを返却すると、interceptorIndexが更新されています。
triggerAfterCompletionでは、interceptorIndexを利用して、preHandleが正常に終了しているInterceptorのafterCompletionメソッドを呼び出しています。