Spring Boot で 例外ハンドリング

Spring Boot での例外ハンドリングの方法について調べたので、そのメモです。
*1

実際に試していないので、問題なく動作するのかはわからないですが、 http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc の内容でほぼ足りそうです。

例外に直接 HTTP Status を紐付けてしまう (Using HTTP Status Codes)

例外クラスに @ResponseStatus をつけることで、Spring MVC の方で処理してくれるっぽいです。

@ResponseStatus(value = NOT_FOUND, reason = "Resource is not found.")
public class ResourceNotFoundException extends RuntimeException {
    ...
}

しかし、これだとエラーレスポンスのボディに JSON で詳細情報を返すのはできなさそう……

Controller クラスごとに例外ハンドリング (Controller Based Exception Handling)

いままで Spring MVC で普通にやっていたのと同じように、Controller クラス内に @ExceptionHandler のついた例外ハンドリングのメソッドを用意するだけです。

@Controller
public class FooController {
    // こんな感じ
    @ExceptionHandler
    @ResponseStatus(NOT_FOUND)
    public void notFound(ResourceNotFoundException ex) {
        // N/A
    }
}

@ResponseBody もつければ、JSON 形式で詳細情報を返すことはできるはず。
しかし、この方法はシステム全体で共通のエラーハンドリングには向かない。
抽象親クラスつくるのはおすすめしない。というか、いまどき共通の機能を抽象親クラスにまとめるとか、ありえんでしょう……
(mix-in ならアリかも?java8ならインタフェースのデフォルト実装の機能を使うことで mix-in のようなことができるので、もしかしたらそれで出来るかも?)

システム全体での例外ハンドリング (Global Exception Handling)

@ControllerAdvice *2をつけたクラスを用意することで、すべての @Controller クラスに対する共通の設定を記述できる。
いってみたら、AspectJ で実装を Weaving するのに似ている。

@ControllerAdvice のクラスでは @ExceptionHandler, @InitBinder, @ModelAttribute アノテーションをつけた三種類のメソッドを定義できるらしい。
@ExceptionHandler だけでなく、@InitBinder も使えるのは嬉しい。空文字だったら null にしてしまうとか、そういうことが一箇所で済む。

@ControllerAdvice
public class FooAdvice {
    @ExceptionHandler
    @RespnseStatus(NOT_FOUND) 
    public void notFound(ResourceNotFoundException ex) {
        // N/A
    }
}

これで、Controller の抽象親クラスを作るというアンチパターンから脱出できる……(といいな)

*1:たぶん、Spring Boot 関係ない。Spring MVC での話ですね……

*2:Spring 3.2 からサポートしていたらしい。 http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ControllerAdvice.html