SpringBootの例外ハンドリング

# SpringBootの例外ハンドリング

## 概要
SpringBootにおいては、SpringMVCの機能を用いた例外ハンドリングやSpringBootのAutoConfigurationと組み込みサーバーを利用した例外ハンドリングなど複数の方法で例外を取り扱うことができる。 
[公式リファレンス](https://docs.spring.io/spring-boot/docs/2.2.0.RELEASE/reference/html/)で述べられているが、ハンドリングが可能な範囲や仕組みが若干わかりにくいので主要な方法をまとめる。


## 例外ハンドリングの方法

### 一覧

|機能名|レイヤ|
|---|---|
|HandlerExceptionResolver|SpringMVC|
|ErrorPage|SpringBoot|
|ErrorController|SpringBoot|

### HandlerExcepitonResolver
``HandlerExceptionResolver``はSpringMVCの機能を利用した例外ハンドリングの方法である。
SpringMVCの機能を利用しているため、FilterやViewで例外が生じた場合はハンドリングできない。

ハンドリングの概略図を下記に示す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/292905/c7a07138-9f68-412d-a89b-43f5d53cdafc.png)

``HandlerExceptionResolver``は下記に示す複数の実装クラスがデフォルトで用意されている。
なお、下記の表において有効/無効はSpringBootのデフォルト設定で有効か無効かを示し、上に来るExceptionResolverが優先される。


|クラス名|有効/無効|機能|
|:-:|:-:|---|
|ExceptionHandlerExceptionResolver|有効|@ExceptionHandlerが付与されたメソッドにより例外ハンドリングを行う|
|ResponseStatusExceptionResolver|有効|@ResponseStatusが付与されている例外クラスが発生した際にハンドリングを行う|
|DefaultHandlerExceptionResolver|有効|SpringMVCで定義されている例外クラスのハンドリングを行う|
|SimpleMappingExceptionResolver|無効|例外クラスとViewを直接マッピングする|

下記に一番オーソドックスな``ExceptionHandlerExceptionResolver``による例外ハンドリングの方法を示す。

#### ExceptionHandlerExceptionResolverによるハンドリング
例外発生時に、発生した例外クラスのメタ情報が付与された@ExceptionHandlerアノテーションを持つメソッドを走査し、処理が移譲される。

@ExceptionHandlerを付与したメソッドは下記に示すように@Controllerもしくは@ControllerAdviceを付与したクラス内に定義できる。

```Java:ThrowExceptionController.java
@Controller
@RequestMapping("web")
public class ThrowExceptionController {
	
	@GetMapping("original")
	public void throwOriginalException() {
		throw new OriginalWebException("thrown in ThrowExceptionController");

	}
	
	@ExceptionHandler(OriginalWebException.class)
	public String handleOriginalWebException(OriginalWebException exception) {
		return "OriginalWebException.html";
	}

}
```

```Java:TransverseExceptionHandler.java
@ControllerAdvice
public class ThrowExceptionController {
	
	@ExceptionHandler(OriginalWebException.class)
	public String handleOriginalWebException(OriginalWebException exception) {
		return "OriginalWebException.html";
	}

}
```

@Controllerの場合、例外ハンドリングが可能なのは同一Controllerで発生した例外だけだが、@ControllerAdviceの場合は全てのControllerを横断的にハンドリングできる。

また、@Controllerと@ControllerAdviceどちらでもハンドリング可能な場合は@Controllerに付与された方が優先される。

### ErrorPage

SpringBootにおけるErrorPageは従来のSpringFWのWeb.xmlで定義していたErrorPage要素とほぼ等しい。

ErrorPageはステータスコードか例外クラスとパスをマッピングし、ハンドリングを行えるようにする。
概略図を下記に示す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/292905/ef5fbdde-4946-cb04-1aad-37bc0988619f.png)


ErrorPageの登録の仕方は下記に示すようにErrorPageRegistrarの実装クラスを作成することで行える。

```Java:AddErrorPage.java

@Configuration
public class AddErrorPage implements ErrorPageRegistrar {

	@Override
	public void registerErrorPages(ErrorPageRegistry registry) {
		registry.addErrorPages(new ErrorPage(exception, path));

        // new ErrorPage(HttpStatusCode, path)でも可能
	}
}

```


### ErrorController
ErrorControllerはSpringBootによって提供される例外ハンドリングの手法であり、SpringBootの例外ハンドリングの中で最も一般的にとられる手法である。
概略図を下記に示す。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/292905/b58ae011-49c2-6838-29e2-b74b6a8b1e25.png)

SpringBootのアプリケーションで生じる例外がハンドリングされなかった場合、上述のErrorPage機能によって全て``/error``のパスへディスパッチされることになっている。
``/error``はデフォルトでは``BasicErrorController``クラスがマッピングされており、そこでハンドリングが行われる。

``BasicErrorController``では、ハンドリングはリクエストヘッダーによってHTMLかJSONを返却する。
HTMLの場合は``WhiteLabelErrorPage``が返却され、返却される内容は``ErrorAttributes``によって規定されている。
JSONの場合は``ErrorAttributes``が返却される。

``ErrorController``によるハンドリング方法をカスタマイズする場合は``ErrorController``の実装クラスを作成する。
また、返却内容自体をカスタマイズする場合は``ErrorAttributes``クラスを実装する。


## まとめ
SpringBootアプリケーションにおける例外ハンドリングは、基本的には``ErrorController``を用い、より細かいハンドリングを行いたい場合は``HandlerExceptionResolver``を用いると良い。