Spring Bootで例外処理

Spring Bootで例外処理

エラーページの表示

spring bootでは、Thymeleafを使用している場合、デフォルトで例外発生時にはsrc/main/resources/template/error.htmlに遷移する。
上記のパスにファイルが存在しない場合は、springが準備したWhitelabel Error Pageに遷移する。


ControllerAdviceを使用

  • クラスに@ControllerAdviceをつけることで全てのコントローラーを対象に例外処理を行うことができる。
  • このクラス内の@ExceptionHandlerをつけたメソッドで、実際に例外発生時に行う処理を記述する。
  • @ExceptionHandlerの引数に指定した例外がスローされた場合にメソッドが実行される。
  • 引数に与える例外クラスは複数記述することができる。
  • 例外の親クラスを引数に設定した場合は、その例外クラスの子の例外が発生した場合も捕捉される。
    (例:引数にRuntimeException.classを指定時にNullPointerExceptionが発生しても例外処理メソッドが実行される)

以下の実装例では、IllegalArgumentExceptionが発生した際に、src/main/resources/template/exception/runtime.htmlに遷移する。

@ControllerAdvice
public class ControllerAdviceSample {

    @ExceptionHandler({ IllegalArgumentException.class })
    public String testExceptionHandle(IllegalArgumentException e, Model model) {
        String stackTrace = ExceptionUtils.getStackTrace(e);
        model.addAttribute("message0", e);
        model.addAttribute("message1", stackTrace);
        return "/exception/runtime";
    }
}

フィルターで発生した例外を捕捉する

上のControllerAdviceでは、フィルターで発生した例外を処理することができない。
フィルターで発生した例外を処理するためには、フィルターで発生した例外を捕捉するためのフィルターを作成する。
例外捕捉用のフィルターの実装例を以下に示す。

public class ExceptionHandleFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        try {
            // 後続のフィルターを実行する
            chain.doFilter(request, response);
        } catch (Throwable e) {
            // 例外発生時の処理を記述する
            // サンプルではRequestMappingが「/error_filter」であるコントローラのメソッド
            // を呼び出してエラー画面へ遷移している
            RequestDispatcher dispatcher = request.getRequestDispatcher("/error_filter");
            request.setAttribute("exception", e);
            dispatcher.forward(request, response);
        }
    }
}
@Controller
public class SampleFilterErrorController {

    // フィルターで例外発生時に呼び出されるメソッド。
    // src/main/resources/template/exception/filter.htmlに遷移する
    @RequestMapping(value = "/error_filter")
    public String result(Model model, HttpServletRequest req) {
        Exception ex = (Exception) req.getAttribute("exception");
        String stackTrace = ExceptionUtils.getStackTrace(ex);

        model.addAttribute("message0", ex.toString());
        model.addAttribute("message1", stackTrace);
        return "/exception/filter";
    }
}

このフィルターは、後続のフィルターで発生した例外しか捕捉することが出来ないため、 フィルターで1番最初に実行されるように別途Configクラスに設定が必要。
setOrder(int)に設定する数字は負数でも可。値が小さい順にFilterの順序付けがされる。
順序を指定しなかった場合は、順序は不定となる。

@Configuration
public class SampleConfig {

    // フィルターをDIコンテナに登録する
    @Bean
    public Filter sampleFilter() {
        return new SampleFilter();
    }

    // 例外処理フィルターをDIコンテナに登録する
    @Bean
    public Filter exceptionHandleFilter() {
        return new ExceptionHandleFilter();
    }

    @Bean
    public FilterRegistrationBean<Filter> sampleFilter0(Filter sampleFilter) {
        FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<Filter>(sampleFilter);
        bean.addUrlPatterns("/*");
        bean.setOrder(2);
        return bean;
    }

    @Bean
    public FilterRegistrationBean<Filter> exceptionHandleFilter0(Filter exceptionHandleFilter) {
        FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<Filter>(exceptionHandleFilter);
        bean.addUrlPatterns("/*");
        // フィルターの実行順序を1に設定する
        bean.setOrder(1);
        return bean;
    }
}