一、Springboot处理异常的三种处理方式
1、在Controller上标注@ExceptionHandler异常。
2、全级别异常处理,通过实现HandlerExceptionResolver 接口,接着重写方法,在该方法中处理所有的异常!
3、全局级别异常处理器:@ControllerAdvice+@ExceptionHandler,推荐使用这种方式!!!
二、介绍第三种方法(@ControllerAdvice+@ExceptionHandler)
2.1、全局捕捉异常处理的相关注解
现在对于异常处理普通方法使用@ControllerAdvice+@ExceptionHandler。
介绍几个注解的含义:
@ControllerAdvice:捕捉所有标注@Controller类抛出的异常。
@ExceptionHandler:标记了使用 errorHandlerOverJson() 方法来处理 GlobalErrorInfoException 异常。通常用于指定拦截指定的异常。
@RestControllerAdvice :该注解其实是由@ControllerAdvice与@ResponseBody组成的,我们一般后台捕捉到异常时都会向前端返回json字符串,所有可以直接使用该注解来标注于类,而不用在每个方法上加@ResponseBody。
2.2、响应码设计
ErrorInfoInterface:错误码接口规范,得到code响应码,message响应描述。
CityErrorInfoEnum与GlobalErrorInfoEnum:这两个都是枚举类,都继承了错误码接口规范,我们可以创建多个枚举类来表示不同的情况描述,这两个类一个表示城市错误信息,另一个表示去全局错误信息枚举类!!!
BaseErrorInfoInterface.java:错误码接口规范
/** * @author changlu * @date 2021/07/22 17:03 **/ public interface BaseErrorInfoInterface { /** * 得到错误码 * @date 2021/07/22 17:04 * @return java.lang.String */ String getResultCode(); /** * 得到错误信息 * @date 2021/07/22 17:05 * @return java.lang.String */ String getResultMsg(); }
commonInfoEnum.java:普通错误枚举类
public enum CommonEnum implements BaseErrorInfoInterface{ SUCCESS("200", "成功"), BODY_NOT_MATCH("400", "请求的数据格式不符"), SIGNATURE_NOT_MATCH("401", "请求的数字签名不匹配!"), OT_FOUND("404", "未找到该资源!"), INTERNAL_SERVER_ERROR("500", "服务器内部错误!"), SERVER_BUSY("503", "服务器正忙,请稍后再试!"), NULLPOINTER_ERROR("1001","空指针异常"); //错误码 private String resultCode; //描述信息 private String resultMsg; CommonEnum(String resultCode, String resultMsg) { this.resultCode = resultCode; this.resultMsg = resultMsg; } @Override public String getResultCode() { return resultCode; } @Override public String getResultMsg() { return resultMsg; } }
之后我们若是出现了某种异常情况,可直接拿到对应的枚举实例,也就直接拿到了code与message。
2.3、结果响应类(最终返回给前端)
该类有三个值组成:code、message、data,分别是响应码、描述信息以及data数据(是一个对象)。
最终通过转成JSON字符串形式返回给前端!
ResultBody.java
/** * @author changlu * @date 2021/07/22 17:15 **/ @Data public class ResultBody { /** * 响应码 */ private String code; /** * 响应消息 */ private String message; /** * 响应结果 */ private Object result; public ResultBody() { } /** * 响应码与响应结果封装 */ public ResultBody(BaseErrorInfoInterface baseErrorInfoInterface) { this.code = baseErrorInfoInterface.getResultCode(); this.message = baseErrorInfoInterface.getResultMsg(); } /** * 成功 * @param data 数据 * @return xyz.changlu.util.ResultBody */ public static ResultBody success(Object data){ ResultBody resultBody = new ResultBody(CommonEnum.SUCCESS); resultBody.setResult(data); return resultBody; } /** * 错误 * @param baseErrorInfoInterface 枚举类 * @return xyz.changlu.util.ResultBody */ public static ResultBody error(BaseErrorInfoInterface baseErrorInfoInterface){ ResultBody resultBody = new ResultBody(baseErrorInfoInterface); resultBody.setResult(null); return resultBody; } /** * 错误 * @param code 状态码 * @param message 描述信息 * @return xyz.changlu.util.ResultBody */ public static ResultBody error(String code,String message){ ResultBody resultBody = new ResultBody(); resultBody.setCode(code); resultBody.setMessage(message); resultBody.setResult(null); return resultBody; } }
2.4、核心:自定义异常类与全局异常捕捉类
自定义异常类:通常自定义异常类继承运行时异常RuntimeException,因为这一类异常是不会强制程序员写try-catch进行捕获的,所以我们可以定义一些来进行手动抛出,之后供全局异常进行捕捉,可设置多个。
全局异常捕捉类:该类用于捕捉@controller注解标注的类抛出的异常,包含自定义异常以及其他异常,我们都可以进行单独编写,一般就这两种。
自定义异常类
public class MsgException extends RuntimeException{ /** * 错误代码 */ private String errorCode; /** * 错误信息 */ private String errorMsg; public MsgException() { } public MsgException(String errorCode, String errorMsg) { this.errorCode = errorCode; this.errorMsg = errorMsg; } public MsgException(String message,String errorCode, String errorMsg) { super(message); this.errorCode = errorCode; this.errorMsg = errorMsg; } //传入错误信息接口(如枚举类) public MsgException(BaseErrorInfoInterface baseErrorInfoInterface,Throwable cause){ super(baseErrorInfoInterface.getResultCode(), cause); this.errorCode = baseErrorInfoInterface.getResultCode(); this.errorMsg = baseErrorInfoInterface.getResultMsg(); } public MsgException(Throwable cause, String errorCode, String errorMsg){ super(errorCode, cause); this.errorCode = errorCode; this.errorMsg = errorMsg; } //set/get方法 public String getErrorCode() { return errorCode; } public void setErrorCode(String errorCode) { this.errorCode = errorCode; } public String getErrorMsg() { return errorMsg; } public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; } @Override public synchronized Throwable fillInStackTrace() { return super.fillInStackTrace(); } }
全局异常捕捉类
当出现异常后,我们依旧会向前端进行响应返回数据!
/** * @author Administrator * @date 2021/07/22 17:27 **/ @RestControllerAdvice public class MyExceptionHandler { private static final Logger logger = LoggerFactory.getLogger(MyExceptionHandler.class); /** * 自定义的业务异常(自己向外抛出的异常) * @param HttpServletRequest servlet请求 * @param ex 自定义抛出异常 * @return xyz.changlu.util.ResultBody */ @ExceptionHandler(value = MsgException.class) public ResultBody msgExceptionHandler(HttpServletRequest request,MsgException ex){ logger.error("自定义的业务异常拦截处理,原因是:" + ex.getErrorMsg()); return ResultBody.error(ex.getErrorCode(),ex.getErrorMsg()); } /** * 空指针异常处理程序 * * @param req 要求的事情 * @param e e * @return {@link ResultBody} */ @ExceptionHandler(value = NullPointerException.class) public ResultBody exceptionHandler(HttpServletRequest req, NullPointerException e) { logger.error("空指针异常处理!原因是:", e.getMessage()); return ResultBody.error(CommonEnum.NULLPOINTER_ERROR); } /** * 其他异常处理程序 * * @param req 要求的事情 * @param e e * @return {@link ResultBody} */ @ExceptionHandler(value = Exception.class) public ResultBody exceptionHandler(HttpServletRequest req, Exception e) { logger.error("其他异常处理程序!原因是:", e.getMessage()); return ResultBody.error(CommonEnum.INTERNAL_SERVER_ERROR); } }
测试
我们来编写一些Controller来测试全局异常捕捉类是否能够捕捉到指定的异常:
/** * @author changlu * @date 2021/07/22 17:38 **/ @RestController public class UserController { //测试自定义异常 @PostMapping("/user") public boolean add(User user){ System.out.println("开始新增...."); if(user.getName() == null){ //抛出自定义异常 throw new MsgException("-1","用户姓名不能为空!"); } return true; } //测试空指针异常 @DeleteMapping("/user/{id}") public boolean delete(@PathVariable("id")Integer id){ System.out.println("开始删除...."); //抛出空指针 String str = null; str.equals("123"); return true; } //测试其他异常 @PutMapping("/user/{id}") public boolean update(@PathVariable("id")Integer id,User user){ System.out.println("开始更新...."); //程序自己出错 Integer.parseInt("abc123"); return true; } @GetMapping("/user/{id}") public ResultBody query(@PathVariable("id")Integer id){ System.out.println("开始查询...."); return ResultBody.success(null); } }