對於與數據庫相關的 Spring MVC 項目,咱們一般會把 事務 配置在 Service層,當數據庫操做失敗時讓 Service 層拋出運行時異常,Spring 事物管理器就會進行回滾。數據庫
如此一來,咱們的 Controller 層就不得不進行 try-catch Service 層的異常,不然會返回一些不友好的錯誤信息到客戶端。json
可是,Controller 層每一個方法體都寫一些模板化的 try-catch 的代碼,很難看也難維護,特別是還須要對 Service 層的不一樣異常進行不一樣處理的時候。瀏覽器
/** * 手動處理 Service 層異常和數據校驗異常的示例 * @param dog * @param errors * @return */ @PostMapping(value = "") AppResponse add(@Validated(Add.class) @RequestBody Dog dog, Errors errors){ AppResponse resp = new AppResponse(); try { // 數據校驗
BSUtil.controllerValidate(errors); // 執行業務
Dog newDog = dogService.save(dog); // 返回數據
resp.setData(newDog); }catch (BusinessException e){ LOGGER.error(e.getMessage(), e); resp.setFail(e.getMessage()); }catch (Exception e){ LOGGER.error(e.getMessage(), e); resp.setFail("操做失敗!"); } return resp; }
本文講解使用 @ControllerAdvice + @ExceptionHandler 進行全局的 Controller 層異常處理,只要設計得當,就不再用在 Controller 層進行 try-catch 了!app
並且,@Validated 校驗器註解的異常,也能夠一塊兒處理,無需手動判斷綁定校驗結果 BindingResult/Errors 了!工具
1.@ControllerAdvice 註解定義全局異常處理類this
//統一在這個類中處理異常,全局攔截指定的異常
@ControllerAdvice public class ExceptionHandle {
2.@ExceptionHandler 註解聲明異常處理方法spa
//統一在這個類中處理異常,全局攔截指定的異常
@ControllerAdvice public class ExceptionHandle { private static final Logger logger = LoggerFactory.getLogger(ExceptionHandle.class); @ExceptionHandler(value = Exception.class) //申明捕獲那個異常類
@ResponseBody //返回給瀏覽器的是一個json格式,上面又沒有@RestController,因此在此申明@ResponseBody
public Result handle(Exception e) { return 「錯誤!」; } }
方法 handleException() 就會處理全部 Controller 層拋出的 Exception 及其子類的異常。設計
實施統一異常管理的案例:code
1.將返回結果信息進行封裝,新建Result類blog
public class Result<T> { /** * 錯誤碼 */
private Integer code; /** * 提示信息 */
private String msg; /** * 具體內容 */
private T data; public Result() { } public Result(Integer code, String msg) { this(code, msg, null); } public Result(Integer code, String msg, T data) { this.code = code; this.msg = msg; this.data = data; } //。。。 }
2.爲方便對結果信息進行處理,新建結果信息工具類:ResultUtils
public class ResultUtil { public static Result success(Object object) { return new Result(0, "成功", object); } public static Result success() { return success(null); } public static Result error(Integer code, String msg) { return new Result(code, msg); } }
3.爲了方便對異常信息進行處理,自定義異常信息類:GirlException
public class GirlException extends RuntimeException { //這個地方不要寫exception,由於Spring是隻對運行時異常進行事務回滾, //若是拋出的是exception是不會進行事務回滾的。
private Integer code; public GirlException(ResultEnum resultEnum) { this(resultEnum.getMsg(), resultEnum.getCode()); } public GirlException(String message, Integer code) { super(message); this.code = code; } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } }
4.爲方便統一管理異常代碼和信息之間的關係,創建枚舉類
public enum ResultEnum { UNKOWN_ERROR(-1, "未知錯誤"), SUCCESS(0, "成功"), PRIMARY_SCHOOL(10, "你可能還在上小學"), MIDDLE_SCHOOL(16, "你可能還在上初中"), ; private Integer code; private String msg; private ResultEnum(Integer code,String msg) { this.msg = msg; this.code = code; } public String getMsg() { return msg; } public Integer getCode() { return code; } }
5.在service層拋出異常
public void getAge(Integer id) throws Exception { Girl girl = girlRepository.getOne(id); Integer age = girl.getAge(); if (age < 10) { throw new GirlException(ResultEnum.PRIMARY_SCHOOL); } else if (age > 10 && age < 16) { throw new GirlException(ResultEnum.MIDDLE_SCHOOL); } }
6.在controller層繼續拋異常
//統一異常處理,在controller層中的exception傳到exceptionHandler去處理
@GetMapping(value = "girls/getAge/{id}") public void getAge(@PathVariable("id") Integer id) throws Exception { girlService.getAge(id); }
7.在exceptionHandler中處理異常
//統一在這個類中處理異常,全局攔截指定的異常
@ControllerAdvice public class ExceptionHandle { private static final Logger logger = LoggerFactory.getLogger(ExceptionHandle.class); @ExceptionHandler(value = Exception.class) //申明捕獲那個異常類
@ResponseBody //返回給瀏覽器的是一個json格式,上面又沒有@RestController,因此在此申明@ResponseBody
public Result handle(Exception e) { if (e instanceof GirlException) { GirlException girlException = (GirlException) e; return ResultUtil.error(girlException.getCode(), girlException.getMessage()); } logger.error("【系統異常】", e); return ResultUtil.error(ResultEnum.UNKOWN_ERROR.getCode(), ResultEnum.UNKOWN_ERROR.getMsg()); } }