springboot統一異常處理
對於與數據庫相關的 Spring MVC 項目,我們通常會把 事務 配置在 Service層,當數據庫操作失敗時讓 Service 層拋出運行時異常,Spring 事物管理器就會進行回滾。
如此一來,我們的 Controller 層就不得不進行 try-catch Service 層的異常,否則會返回一些不友好的錯誤信息到客戶端。
但是,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 了!
而且,@Validated 校驗器註解的異常,也可以一起處理,無需手動判斷綁定校驗結果 BindingResult/Errors 了!
1.@ControllerAdvice 註解定義全局異常處理類
//統一在這個類中處理異常,全局攔截指定的異常
@ControllerAdvice
public class ExceptionHandle {
2.@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) {
return “錯誤!”;
}
}
方法 handleException() 就會處理所有 Controller 層拋出的 Exception 及其子類的異常。
實施統一異常管理的案例:
1.將返回結果信息進行封裝,新建Result類
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());
}
}
springboot統一異常處理