Spring:使用自定义异常 为什么要使用自定义的异常呢,难道说Java自带的异常不够好么? 因为后端的最终目的是为了和前端进行交互,如果某个程序产生了异常,他只会在后台抛出堆栈信息,而不是以json等形式返回给前端,因此,我们需要一个捕捉报错,并且处理报错信息,以json的形式返回给前端的一个逻辑。 这既能够让前端获取到报错信息,还不会因为异常导致后端结构暴露在外面
实例 1 2 3 4 5 6 7 public class BaseException extends Exception { public BaseException () { } public BaseException (String msg) { super (msg); } }
我们定义了一个名为BaseException的异常,继承于Exception,[[Spring:整体思路讲解]]中很多逻辑都有一个throw new BaseException(“xxx错误信息”)的代码,表示这个异常被我用BaseException捕获到了,就不会产生系统异常的报错。 有一个异常还不够,因为报错有很多种类,比如数据库异常,redis服务异常等等,我们如何识别这些不同的报错信息呢。 因此我们需要一个全局异常处理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BaseException.class) public Result<String> handleBaseException (BaseException ex) { return Result.error(ex.getMessage()); } @ExceptionHandler(DataIntegrityViolationException.class) public Result<String> handleDBException (DataIntegrityViolationException ex) { return Result.error("数据库错误:" + ex.getMessage()); } @ExceptionHandler(Exception.class) public Result<String> handleOtherException (Exception ex) { if (ex.getMessage() != null && ex.getMessage().contains("6379" )) { log.error("Redis未启动!" ); return Result.error("Redis服务未启动!" ); } log.error("未知异常:{}" , ex.getMessage()); return Result.error("服务器内部错误" ); } }
@RestControllerAdvice的作用? 全局异常处理器 + 全局数据绑定 + 全局返回值处理器 定义好BaseException之后就需要一个全局异常处理器对这些异常进行处理
这里我们返回值统一采用了Result进行封装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Data public class Result <T> implements Serializable { private Integer code; private String msg; private T data; public static <T> Result<T> success () { Result<T> result = new Result <T>(); result.code = 1 ; return result; } public static <T> Result<T> success (T object) { Result<T> result = new Result <T>(); result.data = object; result.code = 1 ; return result; } public static <T> Result<T> error (String msg) { Result result = new Result (); result.msg = msg; result.code = 0 ; return result; } }
这样,即使有报错,也会使用BaseException进行抛出,抛出的异常会被GlobalExceptionHandler捕获到,然后
1 2 3 4 @ExceptionHandler public Result<String> exceptionHandler (BaseException ex) { return Result.error(ex.getMessage()); }
使用这个方法统一返回 当然我们也可以捕获其他方法
1 2 3 4 @ExceptionHandler public Result<String> exceptionDBHandler (DataIntegrityViolationException ex) { return Result.error(ex.getMessage()); }
也可以直接捕获Exception,然后对其处理
1 2 3 4 5 6 7 8 9 @ExceptionHandler public Result<String> exceptionHandler (Exception ex) { if (ex.getMessage().contains("6379" )){ log.error("Redis未启动" ); return Result.error("Redis未启动" ); } log.error("异常信息:{}" , ex.getMessage()); return Result.error(ex.getMessage()); }
比如以上代码,通过捕获异常中的内容,判断如果内容包含 6379 就添加一个错误日志 redis未启动,并且使用Result.error进行返回异常
因为产生异常之后,程序就就不会继续向下执行,因此直接走你写好的返回向前端返回。
实际用法 1 2 3 if (userMapper.findByUsername(userDTO.getUsername()) != null ) { throw new BaseException ("用户名已存在" ); }
这样前端就会接收到:
1 2 3 4 5 { "code" : 0 , "msg" : "用户名已存在" , "data" : null }
这种不在方法中针对性写出的,而是独立于业务逻辑,集中对某些逻辑进行处理的思想被称为AOP 面相切面编程(挖坑
曾经代码的不足 1. Result泛型封装不止0和1,也可以加上状态码常量 业务异常(400) 权限异常(401) 系统异常(500) 或者单独定义一个通用常亮 1 2 3 4 5 6 public class ResultCode { public static final int SUCCESS = 1 ; public static final int FAIL = 0 ; public static final int UNAUTHORIZED = 401 ; public static final int SERVER_ERROR = 500 ; }
2. 可以直接使用RuntimeException ,运行时异常 我们继承的是Exception,这是一种受检异常 ,每次用的时候都需要 throws
或者 try-catch
而继承RuntimeException后则不用这么麻烦
1 2 3 4 5 public class BaseException extends RuntimeException { public BaseException (String msg) { super (msg); } }