前言
- 我們?nèi)粘5拈_發(fā)中,不管是對(duì)底層數(shù)據(jù)庫操作跺株,還是業(yè)務(wù)層或控制層操作复濒,都會(huì)不可避免地遇到各種可預(yù)知的、不可預(yù)知的異常需要處理乒省。
- 如果每個(gè)過程都單獨(dú)處理異常巧颈,那么系統(tǒng)的代碼耦合度高,工作量大且不好統(tǒng)一袖扛,以后維護(hù)的工作量也很大砸泛。
- 如果能將所有類型的異常處理從各層中解耦出來十籍,這樣既保證了相關(guān)處理過程的功能單一,又實(shí)現(xiàn)了異常信息的統(tǒng)一處理和維護(hù)唇礁。
上面闡述的問題勾栗,我們?cè)谑褂肧pringBoot之后都能解決,我們可以使用如下3種方式處理異常:
- 使用@ExceptionHandler注解
- 實(shí)現(xiàn)HandlerExceptionResolver接口
- 使用@ControllerAdvice注解+@ExceptionHandler注解
1. 使用@ExceptionHandler注解
假設(shè)前端發(fā)送請(qǐng)求后端盏筐,然后后端處理的時(shí)候發(fā)生異常围俘,這時(shí)可以有三種方式通知前端:
- 返回異常頁面(不包含錯(cuò)誤信息)。下面返回"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>
正在處理中界牡,請(qǐng)稍等...
</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}">正在處理中,請(qǐng)稍等...</div>
</body>
</html>
在上面我們把異常信息存儲(chǔ)在Model纳令,然后在異常頁面顯示異常信息挽荠。
在這里插入圖片描述
- 返回JSON格式數(shù)據(jù)。在前后端分離的情況下泊碑,大多都是返回JSON格式數(shù)據(jù)坤按,這里我們規(guī)定如果出現(xiàn)異常,也返回JSON格式數(shù)據(jù)馒过。
響應(yīng)實(shí)體類
@Data
public class MyResponse<T> {
private Long statusCode; //響應(yīng)狀態(tài)碼
private T data; //響應(yīng)數(shù)據(jù)
}
@ExceptionHandler(RuntimeException.class)
//表示返回JSON格式數(shù)據(jù)
@ResponseBody
public MyResponse<String> exception(RuntimeException e){
//在控制臺(tái)打印
e.printStackTrace();
MyResponse<String> response = new MyResponse();
//出現(xiàn)的異常都返回500狀態(tài)碼
response.setStatusCode(500);
response.setData(e.getMessage());
return response;
}
@RequestMapping("/exception")
public void exception(){
int i = 5/0;
}
這樣我們?cè)诎l(fā)生異常的時(shí)候臭脓,也能返回JSON數(shù)據(jù)了。
在這里插入圖片描述
注意點(diǎn)
- 使用@ExceptionHandler注解有一個(gè)不好的地方就是:進(jìn)行異常處理的方法必須與出錯(cuò)的方法在同一個(gè)Controller里面腹忽。
- 這種方式不能實(shí)現(xiàn)全局異常處理来累。
2.實(shí)現(xiàn)HandlerExceptionResolver接口
- 這種方式可以實(shí)現(xiàn)全局的異常控制窘奏,只要在系統(tǒng)運(yùn)行中發(fā)生異常嘹锁,它都會(huì)捕獲到。
- 實(shí)現(xiàn)該接口着裹,必須重寫resolveException方法领猾,該方法就是異常處理邏輯,只能返回ModelAndView 對(duì)象骇扇。
@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需要進(jìn)行異常處理的方法必須與出錯(cuò)的方法在同一個(gè)Controller里面摔竿。那么當(dāng)代碼加入了 @ControllerAdvice,則不需要必須在同一個(gè)controller中了少孝。
- 從名字上可以看出大體意思是控制器增強(qiáng)继低。 也就是說,@controlleradvice+@ExceptionHandler也可以實(shí)現(xiàn)全局的異常捕捉稍走。
@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("參數(shù)解析失敗", 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("不支持當(dāng)前請(qǐng)求方法",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("不支持當(dāng)前媒體",e);
return new ExceptionResponse("content_type_not_supported");
}
/**
* 500:服務(wù)器內(nèi)部異常
* @param e
* @return
*/
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ExceptionHandler
public ExceptionResponse internalServerError(Exception e){
log.error("服務(wù)器內(nèi)部異常",e);
return new ExceptionResponse("你好,請(qǐng)稍等會(huì)...");
}
}
- 在上面代碼中粱胜,定義了捕獲各種異常處理方法柄驻,不同的類型異常由不同的異常處理方法進(jìn)行處理。
- 需要注意一點(diǎn)年柠,就是SpringBoot默認(rèn)不支持捕獲404異常凿歼,需要添加下面兩行配置才能使捕獲404異常生效。
#出現(xiàn)錯(cuò)誤時(shí), 直接拋出異常
spring.mvc.throw-exception-if-no-handler-found=true
#不要為我們工程中的資源文件建立映射
spring.resources.add-mappings=false
測(cè)試404異常以及500異常
- 在瀏覽器中輸入:http://localhost:8888/exception/404exception冗恨,就會(huì)報(bào)404異常答憔,由上面定義的異常處理方法捕獲
在這里插入圖片描述 - 在瀏覽器輸入:http://localhost:8888/exception,就會(huì)報(bào)500異常掀抹,也是由上面定義的異常處理方法捕獲
在這里插入圖片描述
上面我們是具體的異常類型分別定義異常捕獲方法虐拓,我們也可以不那樣做,不區(qū)分類型捕獲全部異常
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler
@ResponseBody
String globalHandleException(Exception e){
return "Exception Deal! " + e.getMessage();
}
}
上面的全局異常處理方法我們都是返回JSON數(shù)據(jù)傲武。我們只需要把方法返回值修改為ModelAndView蓉驹,也能返回視圖域與模型數(shù)據(jù)了。
這樣介紹完了SpringBoot全局異常處理機(jī)制揪利,上面所說的幾乎包含了開發(fā)中常見的異常處理方式态兴。
如果大家覺得不錯(cuò)的話,可以??或者關(guān)注一下博主我疟位。