我們知道,系統中異常包括:編譯時異常和運行時異常RuntimeException,前者通過捕獲異常從而獲取異常信息,后者主要通過規范代碼開發、測試通過手段減少運行時異常的發生。在開發中,不管是dao層、service層還是controller層,都有可能拋出異常,在springmvc中,能將所有類型的異常處理從各處理過程解耦出來,既保證了相關處理過程的功能較單一,也實現了異常信息的統一處理和維護。這篇博文主要總結一下SpringMVC中如何統一處理異常。
異常處理思路
首先來看一下在springmvc中,異常處理的思路
Spring MVC處理異常有4種方式:
(1)使用Spring MVC提供的簡單異常處理器SimpleMappingExceptionResolver;
(2)實現Spring的異常處理接口HandlerExceptionResolver 自定義自己的異常處理器;
(3)使用@ExceptionHandler注解實現異常處理;
(4)使用@ControllerAdvice + @ExceptionHandler
下面使用 @ControllerAdvice + @ExceptionHandler來實現
通過 @ControllerAdvice 注解,我們可以在一個地方對所有 @Controller 注解的控制器進行管理。
注解了 @ControllerAdvice 的類的方法可以使用 @ExceptionHandler、 @InitBinder、 @ModelAttribute 注解到方法上,這對所有注解了 @RequestMapping 的控制器內的方法都有效。
1:@ExceptionHandler:用於捕獲所有控制器里面的異常,並進行處理。
2:@InitBinder:用來設置 WebDataBinder,WebDataBinder 用來自動綁定前台請求參數到 Model 中。
3:@ModelAttribute:@ModelAttribute 本來的作用是綁定鍵值對到 Model 里,此處是讓全局的@RequestMapping 都能獲得在此處設置的鍵值對。
本文使用 @ControllerAdvice + @ExceptionHandler 進行全局的 Controller 層異常處理。只要設計得當,就再也不用在 Controller 層進行 try-catch 了!
一、經典案例
需求:希望通過全局統一的異常處理將自定義錯誤碼以json的形式發送給前端。
1、統一返回結果類 ApiResult
首先,定義一個統一結果返回類,最終需要將這個結果類的內容返回給前端。:
/** * Api統一的返回結果類 */ public class ApiResult { /** * 結果碼 */ private String code; /** * 結果碼描述 */ private String msg; public ApiResult() { } public ApiResult(ResultCode resultCode) { this.code = resultCode.getCode(); this.msg = resultCode.getMsg(); } /** * 生成一個ApiResult對象, 並返回 * * @param resultCode * @return */ public static ApiResult of(ResultCode resultCode) { return new ApiResult(resultCode); } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } @Override public String toString() { return "ApiResult{" + "code='" + code + '\'' + ", msg='" + msg + '\'' + '}'; } }
2、錯誤碼枚舉類 ResultCode
有了 ApiResult
,接下來需要定義一個枚舉類, 來包含所有自定義的結果碼。
/** * 錯誤碼 */ public enum ResultCode { SUCCESS("0", "success"), UNKNOWN_ERROR("0x10001", "unkonwn error"), USERNAME_ERROR("0x10002", "username error or does not exist"), PASSWORD_ERROR("0x10003", "password error"), USERNAME_EMPTY("0x10004", "username can not be empty"); private String code; private String msg; ResultCode(String code, String msg) { this.code = code; this.msg = msg; } public String getCode() { return code; } public String getMsg() { return msg; } }
3、自定義業務異常類 BusinessRuntimeException
接下來需要定義我們自己的業務異常類,以后和業務相關的異常通通拋出這個異常類,我們將錯誤碼枚舉變量的值存於其中。
/** * 自定義業務異常 */ public class BusinessRuntimeException extends RuntimeException{ private String code; private String msg; private ResultCode resultCode; public BusinessRuntimeException(ResultCode resultCode) { super(resultCode.getMsg()); this.code = resultCode.getCode(); this.msg = resultCode.getMsg(); this.resultCode = resultCode; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public ResultCode getResultCode() { return resultCode; } public void setResultCode(ResultCode resultCode) { this.resultCode = resultCode; } }
4、全局異常處理類 GlobalExceptionResolver
最后便是定義全局異常處理類。
1:通過 @ControllerAdvice 指定該類為 Controller 增強類。
2:通過 @ExceptionHandler 自定捕獲的異常類型。
3:通過 @ResponseBody 返回 json 到前端。
注意一點:被@ControllerAdvice注解的全局異常處理類也是一個 Controller ,我們需要配置掃描路徑,確保能夠掃描到這個Controller。
/** * 全局Controller層異常處理類 */ @ControllerAdvice public class GlobalExceptionResolver { /** *處理所有不可知異常 * @param e 異常 * @return json結果 */ @ExceptionHandler(Exception.class) @ResponseBody public ApiResult handleException(Exception e) { System.out.println(e.getMessage()); return ApiResult.of(ResultCode.UNKNOWN_ERROR); } /** * 處理所有業務異常 * @param e 業務異常 * @return json結果 */ @ExceptionHandler(BusinessRuntimeException.class) @ResponseBody public ApiResult handleOpdRuntimeException(BusinessRuntimeException e) { System.out.println(e.getMessage()); return ApiResult.of(e.getResultCode()); } }
二、測試
1、測試 TestExceptionController
@Controller @RequestMapping("/test/") public class TestExceptionController { /** * 測試返回異常信息 * @return */ @RequestMapping(value = "exception.do",method = RequestMethod.GET) public String returnExceptionInfo() { if (1 != 2) { // 用戶民錯誤或不存在異常 throw new BusinessRuntimeException(ResultCode.USERNAME_ERROR); } return "success"; } }
效果:
其他比較全的文章可以看這個: SpringMVC中的統一異常處理