アドバイスの作成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

results matching ""

    No results matching ""