SpringMvc的Controller中異常處理

Spring中默認的異常處理

在Spring中,有一些異常會默認映射爲HTTP狀態碼,不須要程序處理。下表列出Spring的默認處理異常:java

Spring異常 HTTP狀態碼
BindException 400 - Bad Request
ConversionNotSupportedException 500 - Internal Server Error
HttpMediaTypeNotAcceptableException 406 - Not Acceptable
HttpMediaTypeNotSupportedException 415 - Unsupported Media Type
HttpMessageNotReadableException 400 - Bad Request
HttpMessageNotWritableException 500 - Internal Server Error
HttpRequestMethodNotSupportedException 405 - Method Not Allowed
MethodArgumentNotValidException 400 - Bad Request
MissingServletRequestParameterException 400 - Bad Request
MissingServletRequestPartException 400 - Bad Request
NoSuchRequestHandlingMethodException 404 - Not Found
TypeMismatchException 400 - Bad Request

上表中的異常會由Spring自身拋出,若是DispatcherServlet處理過程當中或執行校驗時出現問題時則直接返回。例如,若是DispatcherServlet沒法找到適合處理請求的控制器方法,那麼將會拋出NoSuchRequestHandlingMethodException異常,最終的結果就是產生404狀態碼的響應(Not Found)。web

使用@ResponseStatus處理自定義異常

但對於應用程序內部拋出的自定義異常,它就不能處理了。好比service中拋出了一個自定義異常(NullOrgException),咱們對NullOrgException異常添加@ResponseStatus註解,對其指定狀態碼便可。spring

以下面的代碼:mvc

package com.rebecca.springmvc.exception;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

/** * 自定義異常 * @Author: Rebecca * @Description: * @Date: Created in 2019/6/14 17:04 * @Modified By: */

@ResponseStatus(value = HttpStatus.NO_CONTENT, reason = "No Content")
public class NullOrgException extends RuntimeException {
}

複製代碼

使用try {…} catch 手動捕獲異常

定義了上述異常後,只要應用程序中有拋出NullOrgException異常,就會被捕獲並映射爲對應的狀態碼。那麼若是程序不單單須要狀態碼,還要包含所產生的錯誤,那該怎麼辦呢?此時的話,咱們就不能將異常視爲HTTP錯誤了,而是要按照處理請求的方式來處理異常了。app

package com.rebecca.springmvc.controller.exception;

import com.rebecca.springmvc.org.bean.Org;
import com.rebecca.springmvc.org.service.OrgService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller
@RequestMapping("test/exception")
public class ExceptionTestController {
    private Logger logger = LoggerFactory.getLogger(ExceptionTestController.class);

    @Autowired
    private OrgService service;

    @RequestMapping(value = "orgs", method = RequestMethod.GET)
    @ResponseBody
    public List<Org> getOrgs () {
        List<Org> orgs = null;
        try {
            orgs = service.getOrgs();
        } catch (NullOrgException e) {
            logger.error("無組織機構相關數據!",e);
        }
        return orgs;
    }
}
複製代碼

好比上面代碼中的NullOrgException異常,在Spring中沒有默認映射,那最簡單的辦法就是try {…} catch (NullOrgException e) {…}ide

使用@ExceptionHandler處理自定義異常

上述try {…} catch方式不利於代碼維護,好在Spring提供了一種機制,能夠用@ExceptionHandler註解將異常映射爲HTTP狀態碼。下面是使用@ExceptionHandler註解後的方式:spa

package com.rebecca.springmvc.controller.exception;

import com.rebecca.springmvc.org.bean.Org;
import com.rebecca.springmvc.org.service.OrgService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.List;

@Controller
@RequestMapping("test/exception")
public class ExceptionTestController {
    private Logger logger = LoggerFactory.getLogger(ExceptionTestController.class);

    @Autowired
    private OrgService service;

    @RequestMapping(value = "orgs", method = RequestMethod.GET)
    @ResponseBody
    public List<Org> getOrgs () {
        List<Org> orgs = service.getOrgs();
        return orgs;
    }

    @ExceptionHandler(NullOrgException.class)
    public String handleNullOrgException() {
        return "無組織機構相關數據!";
    }
}
複製代碼

上面代碼咱們在handleNullOrgException()方法上添加了@ExceptionHandler註解,當程序拋出NullOrgException異常時,將會委託該方法來處理。它的返回值是String,你也能夠改成其它的返回類型以知足應用程序須要。對於用@ExceptionHandler註解標註的方法來講,它能處理同一個控制器(Controller)中全部處理器方法所拋出的異常。code

爲了不在多個控制器中編寫重複的@ExceptionHandler註解方法,咱們會建立一個基礎的控制器類,全部控制器類要擴展這個類,從而 繼承 通用的@ExceptionHandler方法。繼承

使用@ControllerAdvice註解處理全部的異常

前面咱們說使用@ExceptionHandler註解只能處理一個控制器(Controller)中全部處理器方法所拋出的異常,那麼有沒有一種方法不用集成就可以處理全部控制器中處理器方法所拋出的異常呢?ip

答案是:有!從Spring 3.2開始,這確定是可以實現的,咱們只需將其定義到控制器通知類中便可。

在Spring 3.2以後,爲這類問題引入了一個新的解決方案:控制器通知。

控制器通知(controller advice)是指任意帶有@ControllerAdvice註解的類。

這個類會包含一個或多個以下類型的方法:

  1. @ExceptionHandler註解標註的方法;
  2. @InitBinder註解標註的方法;
  3. @ModelAttribute註解標註的方法。

在帶有@ControllerAdvice註解的類中,上述的這些方法會運用到整個應用程序全部控制器中帶有@RequestMapping註解的方法上。@ControllerAdvice註解自己已經使用了@Component,所以@ControllerAdvice註解所標註的類將會自動被組件掃描獲取到,和有@Component註解的類同樣。@ControllerAdvice最爲實用的一個場景就是將全部的@ExceptionHandler方法收集到一個類中,這樣全部控制器的異常就能在一個地方進行統一處理。例如,咱們想將NullOrgException的處理方法用到整個應用程序的全部控制器上。以下的程序清單展示的AppWideExceptionHandler就能完成這一任務,這是一個帶有@ControllerAdvice註解的類。下面代碼使用@ControllerAdvice,爲全部的控制器處理異常:

package com.rebecca.springmvc.controller.exception;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

/** * 控制器通知類 * @Author: Rebecca * @Description: * @Date: Created in 2019/6/14 16:30 * @Modified By: */
@ControllerAdvice  // 定義控制器類
public class AppWideException {

    // 定義異常處理方法
    @ExceptionHandler(NullOrgException.class)
    public String handleNullOrgException() {
        return "無組織機構相關數據!";
    }
}
複製代碼

如今,若是任意的控制器方法拋出了DuplicateSpittleException,無論這個方法位於哪一個控制器中,都會調用這個duplicateSpittleHandler()方法來處理異常。咱們能夠像編寫@RequestMapping註解的方法那樣來編寫@ExceptionHandler註解的方法。如程序清單7.10所示,它返回「error/duplicate」做爲邏輯視圖名,所以將會爲用戶展示一個友好的出錯頁面。

總結

Spring中的異常處理:

  1. 特定的Spring異常將會自動映射爲指定的HTTP狀態碼;
  2. 異常 上能夠添加@ResponseStatus註解,從而將其映射爲某一個HTTP狀態碼;
  3. 方法 上能夠添加@ExceptionHandler註解,使其用來處理異常。
  4. @ExceptionHandler註解的異常方法提取到@ControllerAdvice註解的類中,整個應用程序生效(該方式只適用於Spring3.2+)。

處理異常的最簡單方式就是將其映射到HTTP狀態碼上,進而放到響應之中。

參考

《Spring實戰第4版》

相關文章
相關標籤/搜索