SpringのHandlerInterceptorの実行順序

Interceptorの実行順序ってどうなってるんだろうと思い、ソースコードを読んだ内容をまとめます。

# Interceptorの基本
HanlderInterceptorには3つのメソッドがあります。
それらが実行されるタイミングは以下の通りです。

| メソッド名 | 実行タイミング |
|:---|:----|
|preHandle | handlerが実行される前に呼び出されます。|
|postHandle | handlerの実行が正常に完了した後に呼び出されます。handlerで処理されない例外が発生した場合は実行されません。|
|afterCompletion| Viewによるrender処理が完了した後に呼び出されます。handlerやViewのrender処理で例外が発生していても実行されます。|

# Interceptorが複数あるとどうなるのか
ここからが本題になってきます。
この辺の処理の流れを理解するには、DispatcherServlet.doDispatchメソッドを追いかけるといいです。

まず、HandlerExecutionChain.applyPreHandleメソッドでpreHandleを実行していることがわかります。

```java
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メソッドから実行されます。

```java
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メソッドから実行されます。

```java
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メソッドを見ればわかりますね。

（再掲）

```java
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メソッドを呼び出しています。