1,默认的异常处理机制
当浏览器客户端请求一个不存在的页面或服务端处理发生异常时,SpringBoot默认会响应一个html文档内容,称作“Whitelabel Error Page”。
2,以下有四种异常处理方式
-
@ExceptionHandler
注解处理局部异常 -
优点:可以自定义存储异常信息的key,和跳转视图的名称。
缺点:异常捕获类必须在controller里面,所以会出现很多异常方法,因为他可以针对每一个异常
@RestController public class TestController { @GetMapping("/test") public String test(){ if(true){ throw new NullPointerException("ExceptionHandler测试异常"); } return "测试成功"; } @ExceptionHandler(value = {NullPointerException.class}) public ResponseEntity<String> handlerException(NullPointerException ex){ return new ResponseEntity<>("我是一个空指针常:"+ex.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR); }
-
使用
@ControllerAdvice
+@ExceptionHandler
注解处理全局异常 -
优点:可以根据不同的异常对不同的异常进行处理
缺点:编写大量的异常处理方法,代码冗余
@RestController public class TestController1 { @GetMapping("/test1") public String test1(){ if(true){ throw new NullPointerException("@ControllerAdvice + @ExceptionHandler测试异常"); } return "测试成功"; } } @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(NullPointerException.class) @ResponseBody //1,创建异常捕获方法 public ResponseEntity<String> handleException(Exception ex){ return new ResponseEntity<>("我是空指针异常,我又来了:"+ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } //还有多个异常处理,可以一直往下面写 }
-
配置
SimpleMappingExceptionResolver
处理异常 -
优点:
(1)配置相对简单,不需要编写大量的代码;
(2)可以添加更多的异常处理映射;
(3)可以在一个地方定义所有异常的处理方式,方便维护和查看
缺点:只能处理简单的异常映射,对于复杂的异常处理场景可能不够灵活,而且这是静态配置
@Bean public SimpleMappingExceptionResolver simpleMappingExceptionResolver(){ SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties properties = new Properties(); properties.setProperty("java.lang.NUllPointerException","error"); properties.setProperty("java.lang.RuntimeException","error"); //设置异常与视图得映射信息 resolver.setExceptionMappings(properties); return resolver; }
-
自定义
HandlerExceptionResolver
类处理异常
优点:
(1)自定义异常可以根据具体业务需求来定义,并提供更有意义和准确描述的错误信息,以便于程序员和其他开发人员理解问题所在;
(2)自定义异常可以携带额外的上下文信息,使得异常处理过程更具可读性和可理解性。
-
利用自定义类处理异常应该继承现有合适的Java标准库中已存在的基础异常类(RuntimeException或Exception)
// 数据操作异常定义 SUCCESS("2000", "请求成功!"), BODY_NOT_MATCH("4000", "请求的数据格式不符!"), SIGNATURE_NOT_MATCH("4001", "请求的数字签名不匹配!"), DATA_EMPTY("4002","数据为空!"), DATA_NOT_ENOUGH("4003", "数据不足!"), NOT_FOUND("4004", "数据不存在或已删除!"), DATA_FOUND("4005", "数据已存在!"), CODE_NOT_MATCH("4006", "验证码错误!"), CODE_TIME_OUT("4007", "验证码已过期!"), PASSWORD_NOT_MATCH("4008", "密码不一致!"), INTERNAL_SERVER_ERROR("5000", "服务器内部错误!"), SERVER_BUSY("5003", "服务器正忙,请稍后再试!");
(1)创建异常接口,提供获取编码和描述方法
public interface BaseErrorInfoInterface { //创建异常接口,提供获取编码和描述方法 /** * 错误码 * @return */ String getResultCode(); /** * 错误描述 * @return */ String getResultMsg(); }
(2)创建异常枚举类,实现异常服务接口类的方法,并且定义数据操作异常的类型
public enum ExceptionEnum implements BaseErrorInfoInterface{ // 数据操作异常定义 SUCCESS("2000", "请求成功!"), BODY_NOT_MATCH("4000", "请求的数据格式不符!"), SIGNATURE_NOT_MATCH("4001", "请求的数字签名不匹配!"), DATA_EMPTY("4002","数据为空!"), DATA_NOT_ENOUGH("4003", "数据不足!"), NOT_FOUND("4004", "数据不存在或已删除!"), DATA_FOUND("4005", "数据已存在!"), CODE_NOT_MATCH("4006", "验证码错误!"), CODE_TIME_OUT("4007", "验证码已过期!"), PASSWORD_NOT_MATCH("4008", "密码不一致!"), INTERNAL_SERVER_ERROR("5000", "服务器内部错误!"), SERVER_BUSY("5003", "服务器正忙,请稍后再试!"); ; //错误码 private String resultCode; //错误信息 private String resultMsg; ExceptionEnum(String resultCode,String resultMsg){ this.resultCode=resultCode; this.resultMsg=resultMsg; } @Override public String getResultMsg() { return resultMsg; } @Override public String getResultCode() { return resultCode; } }
(3)创建自定义异常,继承
RuntimeException
类public class MyException extends RuntimeException{ private static final long serialVersionUID = 1L; /** * 错误码 */ protected String errorCode; /** * 错误信息 */ protected String errorMsg; public MyException() { super(); } public MyException(BaseErrorInfoInterface errorInfoInterface) { super(errorInfoInterface.getResultCode()); this.errorCode = errorInfoInterface.getResultCode(); this.errorMsg = errorInfoInterface.getResultMsg(); } public MyException(BaseErrorInfoInterface errorInfoInterface, Throwable cause) { super(errorInfoInterface.getResultCode(), cause); this.errorCode = errorInfoInterface.getResultCode(); this.errorMsg = errorInfoInterface.getResultMsg(); } public MyException(String errorMsg) { super(errorMsg); this.errorMsg = errorMsg; } public MyException(String errorCode, String errorMsg) { super(errorCode); this.errorCode = errorCode; this.errorMsg = errorMsg; } public MyException(String errorCode, String errorMsg, Throwable cause) { super(errorCode, cause); this.errorCode = errorCode; this.errorMsg = errorMsg; } 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 Throwable fillInStackTrace() { return this; } }
(4)创建返回响应实体类: 属于规范方法返回值结构体的类,也有利于一致化后端所有接口的返回结构,方便前端(客户端)读取所需要的数据;异常处理返回客户端的数据将采用该类的数据结构
@Data @NoArgsConstructor public class ResponseHelper<T> { //响应状态码 private String code; //响应信息 private String msg; //响应数据 private T data; public ResponseHelper(String code,String msg){ this.code=code; this.msg=msg; } public ResponseHelper(String code,String msg,T data){ this.code=code; this.msg=msg; this.data=data; } public static <T> ResponseHelper<T> success() { return new ResponseHelper<T>("200", "success", null); } public static <T> ResponseHelper<T> success(String msg) { return new ResponseHelper<T>("200", msg, null); } public static <T> ResponseHelper<T> success(String code, String msg) { return new ResponseHelper<T>(code, msg, null); } public static <T> ResponseHelper<T> success(T data) { return new ResponseHelper<T>("200", "success", data); } public static <T> ResponseHelper<T> success(String code, String msg, T data) { return new ResponseHelper<T>(code, msg, data); } public static <T> ResponseHelper<T> fail() { return new ResponseHelper<>("500", "Fail", null); } public static <T> ResponseHelper<T> fail(String code, String msg) { return new ResponseHelper<>(code, msg, null); } }
(5) 创建全局异常处理控制类: 处理各类异常的业务(即根据不同的异常类型向客户端返回相应的信息)
@ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(value = {MyException.class}) public @ResponseBody ResponseHelper myExceptionHandler(MyException e) { return ResponseHelper.fail(e.errorCode, e.getErrorMsg()); } }
(6)创建测试接口
@GetMapping("/test3") public ResponseHelper test(){ if (true){ throw new MyException(ExceptionEnum .CODE_NOT_MATCH .getResultCode(), ExceptionEnum.CODE_NOT_MATCH .getResultMsg()); } return ResponseHelper.success("Success"); }