前言
- 我們日常的開發中,不管是對底層數據庫操作,還是業務層或控制層操作,都會不可避免地遇到各種可預知的、不可預知的異常需要處理。
- 如果每個過程都單獨處理異常,那麼系統的代碼耦合度高,工作量大且不好統一,以後維護的工作量也很大。
- 如果能將所有類型的異常處理從各層中解耦出來,這樣既保證了相關處理過程的功能單一,又實現了異常信息的統一處理和維護。
上面闡述的問題,我們在使用SpringBoot之後都能解決,我們可以使用如下3種方式處理異常:
- 使用@ExceptionHandler註解
- 實現HandlerExceptionResolver接口
- 使用@ControllerAdvice註解+@ExceptionHandler註解
1. 使用@ExceptionHandler註解
假設前端發送請求後端,然後後端處理的時候發生異常,這時可以有三種方式通知前端:
- 返回異常頁面(不包含錯誤信息)。下面返回"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>
- 返回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數據了。
注意點
- 使用@ExceptionHandler註解有一個不好的地方就是:進行異常處理的方法必須與出錯的方法在同一個Controller裏面。
- 這種方式不能實現全局異常處理。
2.實現HandlerExceptionResolver接口
- 這種方式可以實現全局的異常控制,只要在系統運行中發生異常,它都會捕獲到。
- 實現該接口,必須重寫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註解
- 上面說到@ExceptionHandler需要進行異常處理的方法必須與出錯的方法在同一個Controller裏面。那麼當代碼加入了 @ControllerAdvice,則不需要必須在同一個controller中了。
- 從名字上可以看出大體意思是控制器增強。 也就是說,@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("你好,請稍等會...");
}
}
- 在上面代碼中,定義了捕獲各種異常處理方法,不同的類型異常由不同的異常處理方法進行處理。
- 需要注意一點,就是SpringBoot默認不支持捕獲404異常,需要添加下面兩行配置才能使捕獲404異常生效。
#出現錯誤時, 直接拋出異常
spring.mvc.throw-exception-if-no-handler-found=true
#不要爲我們工程中的資源文件建立映射
spring.resources.add-mappings=false
測試404異常以及500異常
- 在瀏覽器中輸入:http://localhost:8888/exception/404exception,就會報404異常,由上面定義的異常處理方法捕獲
- 在瀏覽器輸入:http://localhost:8888/exception,就會報500異常,也是由上面定義的異常處理方法捕獲
上面我們是具體的異常類型分別定義異常捕獲方法,我們也可以不那樣做,不區分類型捕獲全部異常
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
@ResponseBody
String globalHandleException(Exception e){
return "Exception Deal! " + e.getMessage();
}
}
上面的全局異常處理方法我們都是返回JSON數據。我們只需要把方法返回值修改爲ModelAndView,也能返回視圖域與模型數據了。
這樣介紹完了SpringBoot全局異常處理機制,上面所說的幾乎包含了開發中常見的異常處理方式。
如果大家覺得不錯的話,可以👍或者關注一下博主我。