1. 程式人生 > >Spring 中的統一異常處理

Spring 中的統一異常處理

在具體的SSM專案開發中,由於Controller層為處於請求處理的最頂層,再往上就是框架程式碼的。因此,肯定需要在Controller捕獲所有異常,並且做適當處理,返回給前端一個友好的錯誤碼。

不過,Controller一多,我們發現每個Controller裡都有大量重複的、冗餘的異常處理程式碼,很是囉嗦。能否將這些重複的部分抽取出來,這樣保證Controller層更專注於業務邏輯的處理,同時能夠使得異常的處理有一個統一的控制中心點。

1. 全域性異常處理

1.1. HandlerExceptionResolver介面

@Component
public class PlatExceptionHandler implements HandlerExceptionResolver {
private static final String TAG = PlatExceptionHandler.class.getName();
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse response, Object o, Exception e) {
Logger.getLogger(TAG).info("PlatExceptionHandler catch exception :"+e);
ModelAndView mv = new ModelAndView();
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding("UTF-8");
try {
response.getWriter().write(e.getMessage());
} catch (IOException ee) {
ee.printStackTrace();
}
return mv;
}
}

使用全域性異常處理器只需要兩步:

實現HandlerExceptionResolver介面。
將實現類作為Spring Bean,這樣Spring就能掃描到它並作為全域性異常處理器載入。

在 resolveException 中實現異常處理邏輯。從引數上,可以看到,不僅能夠拿到發生異常的函式和異常物件,還能夠拿到 HttpServletResponse物件,從而控制本次請求返回給前端的行為。

此外,函式還可以返回一個 ModelAndView 物件,表示渲染一個檢視,比方說錯誤頁面。不過,在前後端分離為主流架構的今天,這個很少用了。如果函式返回的檢視為空,則表示不需要檢視。

1.4. ControllerAdvice

1.4.1. 使用示例

剛才介紹的是 Controller 區域性的異常處理,用於處理該 Controller 內部的特有的異常處理十分有用。

首先,定義一個存放異常處理函式的類,並使用 @ControllerAdvice 修飾。

@ControllerAdvice(assignableTypes = {GlobalExceptionHandlerMixin.class})

public class ExceptionAdvice {

@ExceptionHandler(ErrorCodeWrapperException.class)

@ResponseBody

public ResponseDTO<?> exceptionHandler(ErrorCodeWrapperException e) {

if ((errCodeException.getErrorCode().equals(ErrorCode.SYSTEM_ERROR))) {

log.error(e);

}

return ResponseDTO.ofErroCodeWrapperException(errCodeException);

}

}

@ExceptionHanlder 修飾的方法的寫法和Controller內的異常處理函式寫法是一樣的。

1.4.2. 控制生效的Controller範圍

注意到,我是這樣編寫註解的:

@ControllerAdvice(assignableTypes = {GlobalExceptionHandlerMixin.class})

它用來限定這些異常處理函式起作用的 Controller 的範圍。如果不寫,則預設對所有 Controller 有效。

這也是 ControllerAdvice 進行統一異常處理的優點,它能夠細粒度的控制該異常處理器針對哪些 Controller 有效,這樣的好處是:

一個系統裡就能夠存在不同的異常處理器,Controller 也可以有選擇的決定使用哪個,更加靈活。
不同的業務模組可能對異常處理的方式不同,通過該機制就能做到。
設想一個一開始並未使用全域性異常處理的系統,如果直接引入全域性範圍內生效的全域性異常處理,勢必可能會改變已有 Controller 的行為,有侵入性。

也就是說,如果不控制生效範圍,即預設對所有 Controller 生效。如果控制生效範圍,則預設對所有 Controller 不生效,降低侵入性。

 

以上幾種方式是 Spring 專門為異常處理設計的機制。就我個人而言,由於 ControllerAdvice 具有更細粒度的控制能力,所以我更偏愛於在系統中使用 ControllerAdvice 進行統一異常處理。除了用異常來傳遞系統中的意外錯誤,也會用它來傳遞處於介面行為一部分的業務錯誤。這也是異常的優點之一

 

@ControllerAdvice
public class RRExceptionHandler {
private Logger logger=LoggerFactory.getLogger(getClass());
@ExceptionHandler(Exception.class)
public ResponseEntity<BaseResultVO> handleException(Exception e){
BaseResultVO baseResultVO=new BaseResultVO(ResponseConstant.SYSTERM_EXCEPT.getCode(),e.getMessage());
logger.error(e.getMessage(),e);
return new ResponseEntity(baseResultVO,HttpStatus.OK);
}

@ExceptionHandler(MyException.class)
public ResponseEntity<BaseResultVO> handleException(MyException e){
BaseResultVO baseResultVO=new BaseResultVO(ResponseConstant.SYSTERM_EXCEPT.getCode(),e.getMessage());
logger.error(e.getMessage(),e);
return new ResponseEntity(baseResultVO,HttpStatus.OK);
}
}