一、介绍 Springboot
框架提供两个注解帮助我们十分方便实现全局异常处理器
以及自定义异常
。
@ControllerAdvice
或 @RestControllerAdvice
(推荐)
@ExceptionHandler
二、实现 1. 定义全局异常处理器 定义GlobalExceptionHandler
类,拦截所有异常。@RestControllerAdvice
注解使得你可以在GlobalExceptionHandler
中处理异常,@ExceptionHandle
注解用于将指定异常绑定到处理的函数上。如下使用@ExceptionHandler(Exception.class)
即对所有异常进行捕获处理。
language-java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @RestControllerAdvice @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public RestErrorResponse exception(Exception e){ //record log log.error("系统异常{}", e.getMessage(),e); //decode errorException String errMessage = "系统异常"; return new RestErrorResponse(errMessage); } }
language-java 1 2 3 4 5 6 @Data @AllArgsConstructor public class RestErrorResponse implements Serializable { private String errMessage; }
事实上,写到这里已经可以用了,RestErrorResponse
用来承载错误信息到前端,因为@RestControllerAdvice
已经包含了@ResponseBody
。
2. 自定义异常类 继承RuntimeException
异常类写一个自定义的异常类。这么做主要是能够使用自定义的枚举类来更优雅的抛出错误。
language-java 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 @Data public class XueChengPlusException extends RuntimeException { private String errMessage; public XueChengPlusException() { super(); } public XueChengPlusException(String errMessage) { super(errMessage); this.errMessage = errMessage; } public static void cast(CommonError commonError){ throw new XueChengPlusException(commonError.getErrMessage()); } public static void cast(String errMessage){ throw new XueChengPlusException(errMessage); } }
language-java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Getter public enum CommonError { UNKOWN_ERROR("执行过程异常,请重试。"), PARAMS_ERROR("非法参数"), OBJECT_NULL("对象为空"), QUERY_NULL("查询结果为空"), REQUEST_NULL("请求参数为空"); private String errMessage; private CommonError( String errMessage) { this.errMessage = errMessage; } }
同时,对于GlobalExceptionHandler
也要做一些修改,一方面处理自定义异常,另一方处理其余异常。
language-java 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 @RestControllerAdvice @Slf4j public class GlobalExceptionHandler { @ExceptionHandler(XueChengPlusException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public RestErrorResponse customException(XueChengPlusException e){ //record log log.error("系统异常{}", e.getErrMessage(),e); //decode errorException String errMessage = e.getErrMessage(); return new RestErrorResponse(errMessage); } @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public RestErrorResponse exception(Exception e){ //record log log.error("系统异常{}", e.getMessage(),e); //decode errorException String errMessage = CommonError.UNKOWN_ERROR.getErrMessage(); return new RestErrorResponse(errMessage); } }
三、使用 在程序中任意地方抛出异常,controller
、service
、dao
层都可以,比如
language-java 1 throw new RuntimeException("价格不能为空且必须大于0");
这时走的就是
language-java 1 2 @ExceptionHandler(Exception.class) public RestErrorResponse exception(Exception e)
除此之外,可以这样抛出自定义异常,比如
language-java 1 XueChengPlusException.cast(CommonError.PARAMS_ERROR);
language-java 1 XueChengPlusException.cast("其他的消息");
language-java 1 throw new XueChengPlusException(CommonError.OBJECT_NULL.getErrMessage());
language-java 1 throw new XueChengPlusException("其他的消息");
这时走的就是
language-java 1 2 @ExceptionHandler(XueChengPlusException.class) public RestErrorResponse customException(XueChengPlusException e)
四、疑问
Q:疑问,XueChengPlusException异常类继承自RuntimeException ,而RuntimeException 继承自Exception,为什么触发customException而不是exception?
在这个全局异常处理器中,当抛出一个XueChengPlusException
异常时,它会被customException(XueChengPlusException e)
方法处理,而不是exception(Exception e)
方法。
这是因为Spring框架的异常处理机制会优先匹配最具体的异常类型。在您的代码中,XueChengPlusException
是RuntimeException
(以及Exception
)的子类,因此它更具体。所以,当抛出一个XueChengPlusException
异常时,Spring会优先调用处理XueChengPlusException
的方法,而不是处理Exception
的方法。
这种行为确实表明全局异常处理器有一定的优先级和覆盖逻辑。具体来说,处理器会优先处理更具体的异常类型,如果没有找到匹配的处理器,那么它会寻找处理更一般异常类型的处理器。