項目中出現異常怎麼辦?是時候學會SpringBoot全局異常處理

前言

  1. 我們日常的開發中,不管是對底層數據庫操作,還是業務層或控制層操作,都會不可避免地遇到各種可預知的、不可預知的異常需要處理。
  2. 如果每個過程都單獨處理異常,那麼系統的代碼耦合度高,工作量大且不好統一,以後維護的工作量也很大。
  3. 如果能將所有類型的異常處理從各層中解耦出來,這樣既保證了相關處理過程的功能單一,又實現了異常信息的統一處理和維護。

上面闡述的問題,我們在使用SpringBoot之後都能解決,我們可以使用如下3種方式處理異常:

  1. 使用@ExceptionHandler註解
  2. 實現HandlerExceptionResolver接口
  3. 使用@ControllerAdvice註解+@ExceptionHandler註解

1. 使用@ExceptionHandler註解

假設前端發送請求後端,然後後端處理的時候發生異常,這時可以有三種方式通知前端:

  1. 返回異常頁面(不包含錯誤信息)。下面返回"exception"爲異常視圖名稱(我們自己編寫的異常頁面)。
@Controller
public class ExceptionHandlerController {

    @ExceptionHandler(RuntimeException.class)
    public String exception(Exception e){
        e.printStackTrace();
        return "exception";
    }

    @RequestMapping("/exception")
    public void exception(){
        int i = 5/0;
    }
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>exception</title>
</head>
<body>
    正在處理中,請稍等...
</body>
</html>
  1. 返回ModelAndView。既返回視圖,也返回異常信息。
@ExceptionHandler(RuntimeException.class)
public ModelAndView exception(RuntimeException e){
    ModelAndView mv = new ModelAndView();
    mv.addObject("msg",e.getMessage());
    mv.setViewName("/exception");
    e.printStackTrace();
    return mv;
}

@RequestMapping("/exception")
public void exception(){
    int i = 5/0;
}
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>exception</title>
</head>
<body>
    <div th:text="${msg}">正在處理中,請稍等...</div>
</body>
</html>

在上面我們把異常信息存儲在Model,然後在異常頁面顯示異常信息。
在這裏插入圖片描述
3. 返回JSON格式數據。在前後端分離的情況下,大多都是返回JSON格式數據,這裏我們規定如果出現異常,也返回JSON格式數據。
響應實體類

@Data
public class MyResponse<T> {
    private Long statusCode;	//響應狀態碼
    private T data;	//響應數據
}
@ExceptionHandler(RuntimeException.class)
//表示返回JSON格式數據
@ResponseBody
public MyResponse<String> exception(RuntimeException e){
    //在控制檯打印
    e.printStackTrace();
    MyResponse<String> response = new MyResponse();
    //出現的異常都返回500狀態碼
    response.setStatusCode(500);
    response.setData(e.getMessage());
    return response;
}

@RequestMapping("/exception")
public void exception(){
    int i = 5/0;
}

這樣我們在發生異常的時候,也能返回JSON數據了。
在這裏插入圖片描述
注意點

  1. 使用@ExceptionHandler註解有一個不好的地方就是:進行異常處理的方法必須與出錯的方法在同一個Controller裏面。
  2. 這種方式不能實現全局異常處理。

2.實現HandlerExceptionResolver接口

  1. 這種方式可以實現全局的異常控制,只要在系統運行中發生異常,它都會捕獲到。
  2. 實現該接口,必須重寫resolveException方法,該方法就是異常處理邏輯,只能返回ModelAndView 對象。
@Component
public class MyGlobalException implements HandlerExceptionResolver {

    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest,
                                         HttpServletResponse httpServletResponse, Object o, Exception e) {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg",e.getMessage());
        mv.setViewName("/exception");
        e.printStackTrace();
        return mv;
    }
}

3.使用@ControllerAdvice註解+@ExceptionHandler註解

  1. 上面說到@ExceptionHandler需要進行異常處理的方法必須與出錯的方法在同一個Controller裏面。那麼當代碼加入了 @ControllerAdvice,則不需要必須在同一個controller中了。
  2. 從名字上可以看出大體意思是控制器增強。 也就是說,@controlleradvice+@ExceptionHandler也可以實現全局的異常捕捉。
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExceptionResponse<T> {
    private T data;
}
@ControllerAdvice
@ResponseBody
@Slf4j
public class GlobalExceptionHandle {

    /**
     *  捕獲404異常
     * @return
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public ExceptionResponse notFoundException(NoHandlerFoundException e){
        log.error("資源未找到",e);
        return new ExceptionResponse<>("你好,你要的資源找不到!");
    }

    /**
     * 400——Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
     public ExceptionResponse handleHttpMessageNotReadableException(HttpMessageNotReadableException  e) {
        log.error("參數解析失敗", e);
        return new ExceptionResponse<>("bad request");
    }

    /**
     *  405——Method Not Allowed
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ExceptionResponse<String> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException  e){
        log.error("不支持當前請求方法",e);
        return new ExceptionResponse<>("request_method_not_supported");
    }

    /**
     * 415——Unsupported Media Type
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public ExceptionResponse handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e){
        log.error("不支持當前媒體",e);
        return new ExceptionResponse("content_type_not_supported");
    }

    /**
     * 500:服務器內部異常
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler
    public ExceptionResponse internalServerError(Exception e){
        log.error("服務器內部異常",e);
        return new ExceptionResponse("你好,請稍等會...");
    }
}
  1. 在上面代碼中,定義了捕獲各種異常處理方法,不同的類型異常由不同的異常處理方法進行處理。
  2. 需要注意一點,就是SpringBoot默認不支持捕獲404異常,需要添加下面兩行配置才能使捕獲404異常生效。
#出現錯誤時, 直接拋出異常
spring.mvc.throw-exception-if-no-handler-found=true
#不要爲我們工程中的資源文件建立映射
spring.resources.add-mappings=false

測試404異常以及500異常

  1. 在瀏覽器中輸入:http://localhost:8888/exception/404exception,就會報404異常,由上面定義的異常處理方法捕獲
    在這裏插入圖片描述
  2. 在瀏覽器輸入:http://localhost:8888/exception,就會報500異常,也是由上面定義的異常處理方法捕獲
    在這裏插入圖片描述

上面我們是具體的異常類型分別定義異常捕獲方法,我們也可以不那樣做,不區分類型捕獲全部異常

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler
    @ResponseBody
    String globalHandleException(Exception e){
        return "Exception Deal! " + e.getMessage();
    }
}

上面的全局異常處理方法我們都是返回JSON數據。我們只需要把方法返回值修改爲ModelAndView,也能返回視圖域與模型數據了。
這樣介紹完了SpringBoot全局異常處理機制,上面所說的幾乎包含了開發中常見的異常處理方式。
如果大家覺得不錯的話,可以👍或者關注一下博主我。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章