使用拦截器统一处理异常

作为一个业务仔,在业务接口代码中肯定会遇到处理异常的情况,比如有代码逻辑的异常,业务逻辑的异常等等。这些异常场景是非常非常多的,这么多异常逻辑要处理,就意味着要写很多重复的代码,作为一个有点追求的业务仔,不能只是懂得CURD,当然希望代码看起来简洁、舒服一点。

本文打算分享笔者处理异常情况的演进过程,然后给出统一异常处理的示例。

一开始的方法是定义一个业务异常类,当捕获到业务异常时,使用异常的错误码和错误信息,构造错误提示返回。

/**
* 错误码枚举类
*/
public enum ResponseCode {
    SUCCESS(0, "OK"),
    SERVER_ERROR(1, "server error");
    private int code;
    private String msg;


    ResponseCode(int code, String msg) {
      this.code = code;
      this.msg = msg;
    }
    public int getCode() {
      return code;
    }
    public ResponseCode setCode(int code) {
      this.code = code;
      return this;
    }
    public String getMsg() {
      return msg;
    }
    public ResponseCode setMsg(String msg) {
      this.msg = msg;
      return this;
    }
}


/**
* 自定义业务异常类
*/
public class BizException extends RuntimeException {
  private final int code;
  public BizException(ResponseCode errCode) {
    super(errCode.getMsg());
    this.code = errCode.getCode();
  }
  public BizException(ResponseCode errCode, String msg) {
    super(msg);
    this.code = errCode.getCode();
  }
  
  public BizException(int code, String msg) {
    super(msg);
    this.code = code;
  }
  public int getCode() {
    return this.code;
  }


}


class TestService {
  public void testAMethod() {
    // 业务异常代码
    throw new BizException(ResponseCode.BIZ_CODE);
  }
}


/**
* 接口返回的通用结构HttpResult
* {"code": 0, "msg": "OK"}
*/
class TestController {
  @Autowired
  private TestService testService;
  public HttpResult testA() {
    try {
        testService.testAMethod();
    } catch (BizException e) {
        return HttpResult(e.getCode(), e.getMsg());
    } catch (Exception e) {
        return HttpResult(ResponseCode.SERVER_ERROR);
    }
    return HttpResult(ResponseCode.SUCCESS);
  }
}

后来发现需要进行一次优化,首先,随着业务代码越来越多,这些try…catch看起来就好臃肿了。

其次,在底层代码有异常时也要在外层捕获住,然后一层一层地往外抛,直到业务接口返回处返回错误信息,如果是一个业务逻辑特别复杂的接口,这些异常处理的代码就会遍布整个系统,使得这些异常代码看起来十分不美观,代码可读性也较差。

久而久之,就在想是否有一种跟校验拦截器一样的方法,在某个地方统一处理这些判断,使得代码看起来比较美观。答案是有的,就是使用ExceptionHandler和RestControllerAdvice注解。

首先,定义一个类:SpringMvcExceptionDemo,类加上RestControllerAdvice注解,类里面定义一个方法exceptionHandler,方法前面加上ExceptionHandler注解,然后就可以在方法里面写异常判断逻辑,对异常逻辑进行相应的处理。

具体的实现代码示例如下:

@RestControllerAdvicepublic
class SpringMvcExceptionDemo {


    @ExceptionHandler
    public Object exceptionHandler(Exception e, HttpServletRequest request, HttpServletResponse response) {
      if (e instanceof BizException) {
          BizException be = (BizException) e;
          return new HttpResult(be.getCode(), be.getMessage());
      }
      if (e instanceof IllegalArgumentException) {
          return new HttpResult(ResponseCode.PARAM_ERROR.getCode(), e.getMessage());
      }
      return new HttpResult(ResponseCode.SERVER_ERR);
    }
}


public HttpResult testA() {
    testService.testAMethod();
    return HttpResult(ResponseCode.SUCCESS);
}

这样一来,就只需要在该抛出业务异常的地方抛出异常,由拦截器统一处理异常即可,减少了很多重复代码,同时提高代码的可读性。

RestControllerAdvice 和 ExceptionHandler

RestControllerAdvice是Spring框架中的一个注解,这个注解包含了ControllerAdvice和ResponseBody,帮助我们通过加入一个横切点ExceptionHandler来处理RestfulAPI中的异常。执行的时机是在doDispatch中,调用processDispatchResult方法,如果有异常,则会调用添加了ExceptionHandler注解的方法去判断。

流程图如下:

使用拦截器统一处理异常_第1张图片

核心处理代码:

for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
    exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
    if (exMv != null) {
      break;
    }
}

总结

代码和原理比较简单,统一处理异常的目的只是为了消除重复的代码块,写出更简洁的代码,现在写代码也是坚持这个想法,希望能探索出更多的技巧,有其他技巧的也欢迎一起讨论。

原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

如果本文对你有帮助,麻烦顺手点个赞吧,谢谢

更多精彩内容,请关注个人公众号。

你可能感兴趣的:(使用拦截器统一处理异常)