type
Post
status
Published
date
Nov 2, 2021
slug
summary
介绍SpringBoot项目中通用的统一异常处理方式
tags
项目方案
category
技术分享
icon
password

SpringMVC 异常机制

原则:MVC 三层出现的异常都通过 throws 抛出,最终由前端控制器 DispatcherServlet 捕获,交由异常处理器 HandlerExceptionResolver 处理
  • HandlerExceptionResolver 是异常处理器接口,实现这个接口可以自定义异常处理器
  • HTTP 异常可以在 web.xml 中配置自定义异常页面
    • <!--处理500异常--> <error-page>  <error-code>500</error-code>  <location>/500.html</location> </error-page> <!--处理404异常--> <error-page>  <error-code>404</error-code>  <location>/404.html</location> </error-page>

最佳实践

异常枚举

异常枚举 ExceptionEnum 用于和和前端约定好异常状态码和消息,并传入自定义的异常类进行统一处理
public enum ExceptionEnum {    /*    * 用户模块异常    * */    NEED_USR_NAME(10001,"用户名不能为空"),    /*    * 系统异常*/    SYSTEM_ERROR(20000,"服务器内部异常"); //.....其他    Integer code;    String msg;    ExceptionEnum(Integer code, String msg) {        this.code = code;        this.msg = msg;   }    public Integer getCode() {        return code;   }    public void setCode(Integer code) {        this.code = code;   }    public String getMsg() {        return msg;   }    public void setMsg(String msg) {        this.msg = msg;   } }

封装业务异常类

封装 BusinessException 继承 RuntimeException ,用来规定异常的消息结构,封装异常操作的相关方法
public class BusinessException extends RuntimeException {    /**     * 异常状态码     */    private Integer code = 500;    /**     * 异常描述信息     */    private String msg;    public BusinessException(Integer code, String msg) {        this.code = code;        this.msg = msg;   }    public BusinessException(String msg) {        super(msg);        this.msg = msg;   }    public BusinessException(String msg, Throwable e) {        super(msg, e);        this.msg = msg;   }    public BusinessException(Integer code, String msg, Throwable e) {        super(msg, e);        this.code = code;        this.msg = msg;   }

全局异常处理器

  • 创建 GlobalExceptionHandler 实现 HandlerExceptionResolver接口
    • 重写resolveException方法,并配置为Bean,使用 @ControllerAdvice 注解设置为全局异常处理类
  • @ExceptionHandler 可以指定方法所拦截的异常类型,实现异常的分类处理
@ControllerAdvice public class GlobalExceptionHandler {    private final Logger log= LoggerFactory.getLogger(GlobalExceptionHandler.class);    @ExceptionHandler(Exception.class)    @ResponseBody    public Object handleException(Exception e){        log.error("SYSTEM Exception:"+e);        return ApiCommonResponse.error(ExceptionEnum.SYSTEM_ERROR);   }    @ExceptionHandler(CommonException.class)    @ResponseBody    public Object handleException(CommonException e){        log.error("CommonException:"+e);        return ApiCommonResponse.error(e.getCode(),e.getMessage());   } }
这里使用了 统一返回对象,在另一篇文章中有介绍,如果不使用统一返回对象进行返回,也可以直接返回异常消息
public class GlobalExceptionHandler {    @ResponseBody    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)    @ExceptionHandler(Exception.class)    public String exceptionHandler(Exception e) {        log.error("业务异常", e);        if (e instanceof MethodArgumentNotValidException) {            MethodArgumentNotValidException exception = (MethodArgumentNotValidException) e;            return exception.getBindingResult().getFieldError().getDefaultMessage();       } else if (e instanceof BusinessException) {            BusinessException exception = (BusinessException) e;            return exception.getMsg();       } else if (e instanceof UnauthorizedException) {            return "权限不足";       } else {            BarkMonitorUtil.sendWithGroup("没有预料的异常:" + e.getMessage(), "YOAS办公系统");            return "系统内部错误";       }   } }
并查集实现抵御 XSS 跨站脚本攻击