46
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

SpringMVCのHandlerExceptionResolverの適用順

Last updated at Posted at 2015-08-07

久しぶりに例外周りの設定を見ていたら、ResponseStatusアノテーション付けたときの挙動でちょっとハマりました。
知っている人にはなんてことない話ですが、こういうのよく忘れるタチなので自分用にメモしておきます。

自分がよく使っている例外ハンドリングの復習

ExceptionHandlerアノテーション

Controller内にExceptionHandlerアノテーションを付与したメソッドを定義すると、
そのController内で指定の型の例外がthrowされた場合にハンドリングできる。

@ExceptionHandlerを設定したメソッド
@ExceptionHandler(Exception.class)
public String handleException(final Exception e) {
    return "/hoge/error";
}

HandlerExceptionResolver

これ実装したクラスを作成して

HanlderExceptionResolverを実装したクラス
package jp.hoge.fuga.web.handler;

public class GlobalExceptionHandler implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(final HttpServletRequest request,
            final HttpServletResponse response, final Object handler,
            final Exception e) {

        model.setViewName("/common/error");

        return model;
    }
}

設定しておくと

servlet-context.xml
    <beans:bean class="jp.hoge.fuga.web.handler.GlobalExceptionHandler"/>

前段で処理されてないthrowされた例外がハンドリングできる。

と、この2つの方法については、あまり深いこと考えずに使っていたわけです。

例外クラス自体にResponseStatusアノテーションを設定している場合

今回ちょいハマりしたのですが、独自例外にこういう設定ができますよね。

@ResponseStatusを設定した独自例外
package jp.hoge.fuga.exception;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {

今まで使ったことなかったんですが、これをthrowして指定したレスポンスコード(上記の場合は404)で
アプリケーションから応答返したい、という場合に使ったりするようです。

こうした独自例外をController内でthrowした場合、

  • 例外の型が合っていれば、ExceptionHandlerアノテーション付きのメソッドでハンドリングできる
  • 型に関わらず、独自にHandlerExceptionResolverを実装して設定したクラスではハンドリングできない

という挙動になったので、はて?となったのです。

...で、ここでやっと気付くわけですよ、
「ああ、mvc:annotation-driven指定してるから、デフォルトのHandlerExceptionResolverが設定されているんだよね。
それが作用してるんだよね」
と。

ただ、何がどういう順番で設定されているのかよく分かってなかったので、
デバッグがてら、デフォルトでどういった設定がされるのかを見てみました。
※ spring-webmvc-4.0.6.RELEASEで確認。

Spring MVCのデフォルトのHandlerExceptionResolverの設定

例外発生時に呼び出されるのは、
org.springframework.web.servlet.DispatcherServlet#processHandlerException

ここで
handlerExceptionResolvers
というListを持っていて、この中にHandlerExceptionResolver達が格納されており、
先頭から順に判定してどれを適用するか決めています。

格納順(=適用判定がされる順)は以下の通りでした。

  • org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver
    → ExceptionHandlerアノテーション付きの処理の適用を判定するHandlerExceptionResolver
  • org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver
    → ResponseStatusアノテーションでステータスコードが設定されたものに適用するHandlerExceptionResolver
  • org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
    → Springで定義されている例外に適用するHandlerExceptionResolver
  • 独自にHandlerExceptionResolverを実装して設定したHandlerExceptionResolver

なるほど。
だからResponseStatusアノテーション付与した例外をthrowすると、独自HandlerExceptionResolverまでは到達しなかったんですね
と理解ができました。

ドキュメントとかにも書いてあるのかもしれませんが、実際のソース見ると色々と納得感あるよね、と改めて思った次第です。

46
42
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
46
42

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?