使用攔截器統一處理異常

作爲一個業務仔,在業務接口代碼中肯定會遇到處理異常的情況,比如有代碼邏輯的異常,業務邏輯的異常等等。這些異常場景是非常非常多的,這麼多異常邏輯要處理,就意味着要寫很多重複的代碼,作爲一個有點追求的業務仔,不能只是懂得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註解的方法去判斷。

流程圖如下:

核心處理代碼:

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

總結

代碼和原理比較簡單,統一處理異常的目的只是爲了消除重複的代碼塊,寫出更簡潔的代碼,現在寫代碼也是堅持這個想法,希望能探索出更多的技巧,有其他技巧的也歡迎一起討論。

原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

如果本文對你有幫助,麻煩順手點個贊吧,謝謝

更多精彩內容,請關注個人公衆號。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章