SpringBoot中的全局异常处理
在实际项目中,如果出现了异常,我们不希望直接把异常抛给用户,应该对异常进行处理,然后返回一个友好的信息给用户。这节主要总结一下项目中如何使用SpringBoot如何拦截全局的异常。
1. 定义返回的json结构
请求接口需要返回json数据,一般后台会统一定义一个返回给前端的数据结构,包括code、msg信息等,这可以参考【1】SpringBoot返回Json数据及封装中封装的统一json结构,如下:
public class JsonResult { /** * 异常码 */ protected String code; /** * 异常信息 */ protected String msg; protected JsonResult() {} public JsonResult(String code, String msg) { this.code = code; this.msg = msg; } // get set }
2. 处理系统异常
新建一个GlobalExceptionHandler全局异常处理类,然后加上@ControllerAdvice
注解即可拦截项目中抛出的异常,该注解中也可以跟上basePackages属性,用来指定拦截哪个包中的异常,一般我们可以不指定,所有异常都拦截。
@ControllerAdvice public class GlobalExceptionHandler { }
下面举几个例子来说明一下如何使用。
2.1 处理不合法的请求格式异常
有些时候,请求格式不合法,会抛出TypeMismatchException,我们可以拦截该异常,做一个友好处理:
@ControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * 不合法的请求格式异常 * @param ex * @return */ @ExceptionHandler(TypeMismatchException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST) @ResponseBody public JsonResult handleTypeMismatchException(TypeMismatchException ex) { logger.error("不合法的请求格式", ex); return new JsonResult("400", "不合法的请求格式"); } }
2.2 HTTP参数不可读异常
有些时候,请求参数不合法,会抛出HttpMessageNotReadableException,我们可以拦截该异常,做一个友好处理:
@ControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * HTTP参数不可读 * @param ex * @return */ @ExceptionHandler(HttpMessageNotReadableException.class) @ResponseStatus(value = HttpStatus.BAD_REQUEST) @ResponseBody public JsonResult handleHttpMessageNotReadableException( HttpMessageNotReadableException ex) { logger.error("请求参数不可读", ex); return new JsonResult("400", "请求参数不可读"); } }
2.3 一劳永逸?
当然了,异常很多,比如还有RuntimeException,数据库还有一些查询或者操作异常等等。由于Exception异常是父类,所有异常都会继承该异常,所以我们可以直接拦截Exception异常:
@ControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * 系统异常 预期以外异常 * @param ex * @return */ @ExceptionHandler(Exception.class) @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) @ResponseBody public JsonResult handleUnexpectedServer(Exception ex) { logger.error("系统异常:", ex); return new JsonResult("500", "系统发生异常,请联系管理员"); } }
但是项目中,我们一般都会比较详细的去拦截一些常见异常,拦截Exception虽然可以一劳永逸,但是不利于我们去排查或者定位问题。实际项目中,可以把拦截Exception异常写在GlobalExceptionHandler最下面,如果都没有找到,最后再拦截一下Exception异常,保证输出信息友好。
3. 拦截自定义异常
在实际项目中,除了拦截一些系统异常外,在某些业务上,我们需要自定义一些异常,比如在微服务中,服务之间的相互调用很平凡,很常见。要处理一个服务的调用,那么可能会调用失败,此时我们需要自定义一个异常,当调用失败时抛出来,给GlobalExceptionHandler去捕获。
3.1 定义异常信息
由于在业务中,有很多异常,针对不同的业务,可能给出的提示信息不同,所以为了方便项目管理,我们一般会定义一个异常信息枚举类。
/** * 业务异常提示信息枚举类 * @author shengwu ni */ public enum BusinessMsgEnum { /** 参数异常 */ PARMETER_EXCEPTION("102", "参数异常!"), /** 等待超时 */ SERVICE_TIME_OUT("103", "服务调用超时!"), /** 参数过大 */ PARMETER_BIG_EXCEPTION("102", "输入的图片数量不能超过50张!"), /** 500 : 一劳永逸的提示也可以在这定义 */ UNEXPECTED_EXCEPTION("500", "系统发生异常,请联系管理员!"); // 还可以定义更多的业务异常 /** * 消息码 */ private String code; /** * 消息内容 */ private String msg; private BusinessMsgEnum(String code, String msg) { this.code = code; this.msg = msg; } // set get方法 }
3.2 拦截自定义异常
然后我们定义一个业务异常,当出现业务异常时,我们就抛这个自定义的业务异常即可。
/** * 自定义业务异常 * @author shengwu ni */ public class BusinessErrorException extends RuntimeException { private static final long serialVersionUID = -7480022450501760611L; /** * 异常码 */ private String code; /** * 异常提示信息 */ private String message; public BusinessErrorException(BusinessMsgEnum businessMsgEnum) { this.code = businessMsgEnum.code(); this.message = businessMsgEnum.msg(); } // get set方法 }
构造方法中,传入我们上面自定义的异常枚举类,所以在项目中,如果有信息异常信息,我们直接在枚举类中添加即可,然后再拦截该异常时获取即可。
@ControllerAdvice public class GlobalExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); /** * 拦截业务异常,返回业务异常信息 * @param ex * @return */ @ExceptionHandler(BusinessErrorException.class) @ResponseStatus(value = HttpStatus.OK) @ResponseBody public JsonResult handleBusinessError(BusinessErrorException ex) { String code = ex.getCode(); String message = ex.getMessage(); return new JsonResult(code, message); } }
在业务代码中,我们可以直接在抛出业务异常,测试一下:
@RestController @RequestMapping("/test") public class TestController { @RequestMapping("/exception") public String testException() { try { int i = 1 / 0; } catch (Exception e) { throw new BusinessErrorException(BusinessMsgEnum.UNEXPECTED_EXCEPTION); } return null; } }
运行一下项目,测试一下,返回json如下;
{"code":"500","msg":"系统发生异常,请联系管理员!"}
SpringBoot的全局异常拦截处理就总结这么多,在项目中运用也很广泛,基本上每个项目中都需要全局异常处理。
上一篇: 夏天这一物人人爱吃 却让肠胃长满结石!
下一篇: Shiro权限管理