全局異常處理介紹
說到異常是項目開發中最熟悉不過的了,為了處理異常,我們往往會寫很多類似於下面這樣的代碼:
public void hello() {
try {
// 業務代碼
}catch (Exception e) {
e.printStackTrace();
}
}
這樣寫出的代碼包含了很多異常處理,不僅僅可讀性差,而且包含了需要非業務相關的邏輯。
在項目開發中我們應該將精力放在業務處理上,項目中除了業務相關的代碼,最好不要存在其他無關的代碼。
那么類似於異常這種項目中各個地方都存在的非業務代碼如何處理呢?
首先,異常處理是必不可少的,除了不能影響用戶體驗和前后端交互,也是我們排查問題必不可少的關鍵信息。
其次,我們關注的問題是何時何地處理異常。
如果你感興趣的話,花點時間看看下面內容吧!
全局異常處理實戰
首先拋出幾個概念,方便大家對后面內容的理解
- 采用的是SpringBoot 自帶的異常處理機制
- 對通過Controller接口訪問產生的異常,統一處理,響應結果格式統一
1、准備
(1)創建一個SpringBoot項目,並對外暴露訪問接口
我准備了這樣一個接口,並構造了一個 / by zero
異常,瀏覽器訪問結果:
這樣的接口你要提供出去了,看前端小姐姐不打死你 。。。
2、配置全局異常通知
在SpringBoot 2.X 版本中 @ControllerAdvice
@RestControllerAdvice
注解,可以用於定義 @ExceptionHandler
,並應用到配置了 @RequestMapping
的控制器中。
- @ControllerAdvice 注解:是一個特殊的
@Component
,用於標識一個類,這個類中被以下三種注解標識的方法:@ExceptionHandler
,@InitBinder
,@ModelAttribute
,將作用於所有的@Controller
類的接口上。 - @RestControllerAdvice 注解:是
@ControllerAdvice
+@ResponseBody
的結合體 - @ExceptionHandler 注解:統一異常處理,也可以指定要處理的異常類型
下面我們就實戰一個簡單的全局異常處理:
@RestControllerAdvice
@Log4j2
public class GlobalExceptionHandler {
/**
* 處理任何 Exception 接口實現類異常
*
* @param request
* @param exception
* @return
* @throws Exception
*/
@ExceptionHandler(Exception.class)
public Map exceptionHandler(HttpServletRequest request, Exception exception) throws Exception {
log.error("url:{}|errorMsg:{}", request.getRequestURI(), exception.getMessage(), exception);
Map<String, Object> result = new HashMap<>(3);
result.put("code", 500);
result.put("msg", exception.getMessage());
result.put("url", request.getRequestURL().toString());
return result;
}
}
再次訪問我們的瀏覽器:
剛才的報錯頁面現在就變成了這樣
到此為止,一個簡單的全局異常處理解決方式就完成了,是不是非常簡單,當然這只是最最簡單的版本,現實項目中遠遠比這個復雜,下面我們就來一個全套的項目中異常如何處理的。
全局異常處理實戰進階
在真實的項目開發中,前后端一般是分離的,就表示后端不管出現什么情況(成功、失敗、異常),返回給前端的數據結構一定是統一的,這是約定大於編碼思想的一種體現。
1、統一接口響應
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CommonResult<T> implements Serializable {
/**
* 響應碼
*/
private Integer code;
/**
* 響應信息
*/
private String msg;
/**
* 響應數據
*/
private T data;
public static <T> CommonResult<T> success(T data) {
return new CommonResult(200, "success", data);
}
}
2、自定義異常
為什么要自定義異常呢?
項目中為什么要自定義異常呢,首先如果是項目業務處理產生的異常,通過自定義異常可以知道是哪個項目產生的異常,而且對不同的異常也可以有不同的處理邏輯,也方便后續擴展
public class CustomException extends RuntimeException {
private static final long serialVersionUID = 1L;
//自定義錯誤碼
private Integer code;
//自定義構造器,只保留一個,讓其必須輸入錯誤碼及內容
public CustomException(int code, String msg) {
super(msg);
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
3、改造全局異常處理代碼
@RestControllerAdvice
@Log4j2
public class GlobalExceptionHandler {
/**
* 處理CustomException 異常
*
* @param request
* @param exception
* @return
* @throws Exception
*/
@ExceptionHandler(CustomException.class)
public Map exceptionHandler(HttpServletRequest request, CustomException exception) throws Exception {
log.error("url:{}|errorMsg:{}", request.getRequestURI(), exception.getMessage(), exception);
Map<String, Object> result = new HashMap<>(3);
result.put("code", exception.getCode());
result.put("msg", exception.getMessage());
result.put("url", request.getRequestURL().toString());
return result;
}
/**
* 處理任何 Exception 接口實現類異常
*
* @param request
* @param exception
* @return
* @throws Exception
*/
@ExceptionHandler(Exception.class)
public Map exceptionHandler(HttpServletRequest request, Exception exception) throws Exception {
log.error("url:{}|errorMsg:{}", request.getRequestURI(), exception.getMessage(), exception);
Map<String, Object> result = new HashMap<>(3);
result.put("code", 500);
result.put("msg", exception.getMessage());
result.put("url", request.getRequestURL().toString());
return result;
}
}