アドバイスの作成3 - @ControllerAdvice
コントローラーの、オブジェクトバインディングや、エラーハンドリングに特化したアドバイスを作成するには、@ControllerAdvice
を使用します。
これがどのように動作するのか確認するため、コントローラーアドバイスと、呼び出すと例外をスローするコントローラーを、以下を参考に作成します。
// src/main/java/io/github/yo1000/sss/aspect/ExceptionControllerAdvice.java
package io.github.yo1000.sss.aspect;
import io.github.yo1000.sss.model.Memo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
@ControllerAdvice
public class ExceptionControllerAdvice {
private static final Logger LOGGER = LoggerFactory.getLogger(ExceptionControllerAdvice.class);
@ExceptionHandler({MemoException.class})
@ResponseStatus(HttpStatus.NOT_ACCEPTABLE)
@ResponseBody
public String handleException(MemoException e) {
LOGGER.error("MemoException", e);
return "error!!";
}
@InitBinder
public void initBinder(WebDataBinder binder) {
LOGGER.info("InitBinder");
}
@ModelAttribute
public void modelAttribute(Memo model) {
LOGGER.info("ModelAttribute");
}
public static class MemoException extends RuntimeException {}
}
// src/main/java/io/github/yo1000/sss/controller/page/MemoController.java
package io.github.yo1000.sss.controller.page;
import io.github.yo1000.sss.aspect.ExceptionControllerAdvice;
import io.github.yo1000.sss.model.Memo;
import io.github.yo1000.sss.service.MemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
@Controller
@RequestMapping("memo")
public class MemoController {
// 中略
@RequestMapping("error")
public String getError(Model model) {
throw new ExceptionControllerAdvice.MemoException();
}
}
アプリケーションを再起動して、cURL で、http://localhost:8080/memo/error をリクエストしてみます。
$ curl -v http://localhost:8080/memo/error
* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> GET /memo/error HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 406 Not Acceptable
< Server: Apache-Coyote/1.1
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 7
< Date: Wed, 25 May 2016 05:30:42 GMT
<
* Connection #0 to host localhost left intact
error!!
コントローラーアドバイスで設定したように、406 Not Acceptable
がレスポンスされることを確認できました。
@ControllerAdvice によ割り込み
このように、コントローラーアドバイスを使用すると、コントローラーをまたいで、横断的に様々なハンドリングを記述できるようになります。実際にこれが、コントローラーのどのような箇所に割り込んでいくのか、簡単に紹介します。
コントローラーアドバイスには、@InitBinder
、@ModelAttribute
、@ExceptionHandler
を設定したメソッドを定義できます。これらはそれぞれ、コントローラー内の @RequestMapping
が設定されたメソッドの前後に割り込みをおこないます。
下図は、コントローラーのメソッドに対し、コントローラーアドバイスのメソッドが、どのように割り込むのかを表現したものです。
ここまでで、画面の表示から、値の受け渡し、AOP による責務の分割まで、はじめに「Spring における責務の分割」として紹介した、基本的な責務はすべて紹介しました。ここまでの内容で、ほとんどのアプリケーションは作成できるはずです。
以降の章では付録として、知っていると、開発がより捗る機能群について触れていきます。
アノテーション
今回、新たに登場したアノテーションを簡単に説明しておきます。
@ControllerAdvice
コントローラーアドバイスクラスに設定します。このアノテーションが設定されると、クラスが DI コンテナへの登録対象としてマークされます。
@ExceptionHandler
例外をハンドリングするコントローラーメソッドに設定します。対処する例外クラスごとに、複数定義することができます。
コントローラーでも使用できますが、この場合、そのコントローラー内のみで有効になります。コントローラーアドバイスで設定した場合は、すべてのコントローラーに対して、横断的に作用します。
@ResponseStatus
コントローラーメソッドがレスポンスする、HTTP ステータスコードを設定します。
@ResponseBody
コントローラーによるレスポンスを、そのままレスポンスボディに出力するよう設定します。
@InitBinder
パラメーターの割り当てをハンドリングするメソッドに設定します。
リクエスト時に渡されたパラメーターが、@ModelAttribute
、@PathVariable
、@RequestParam
などにより、コントローラーメソッドの各引数に割り当たる前に割り込めるようになります。
サンプル
ここまでのコードサンプルは、以下より入手できます。
$ git clone -b chapter/10 https://github.com/yo1000/self-study-spring.git