1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        巧用斷言&異常處理類簡化業(yè)務(wù)異常代碼

        共 25302字,需瀏覽 51分鐘

         ·

        2022-07-13 16:36

        點擊上方藍(lán)色字體,選擇“設(shè)為星標(biāo)”


        回復(fù)”學(xué)習(xí)資料“獲取學(xué)習(xí)寶典

        背景


        軟件開發(fā)過程中,不可避免的是需要處理各種異常,所以代碼中就會出現(xiàn)大量的 try {...} catch {...} finally {...} 代碼塊,不僅有大量的冗余代碼,而且還影響代碼的可讀性。

        另一個就是面對業(yè)務(wù)異常的情況,我們經(jīng)常需要將業(yè)務(wù)異常結(jié)果組裝成統(tǒng)一的信息返回給前端進(jìn)行提示。


        假如我們在每個接口中都去包裝異常信息進(jìn)行返回就會讓代碼變得很冗余且混亂。在我司的實際項目開發(fā)過程中,我們會巧用斷言去簡化代碼。


        業(yè)務(wù)異常處理示例


        假設(shè)我們定義的標(biāo)準(zhǔn)接口響應(yīng)實體為 ApiResult:

         @Data
         @Builder
         @NoArgsConstructor
         @AllArgsConstructor
         public class ApiResult<Timplements Serializable {
             private static final long serialVersionUID = 411731814484355577L;
             private int responseCode;
             private String responseMsg;
             private boolean isSuccess;
             private T data;
         
             public String toString() {
                 return "ApiResult(responseCode=" + this.getResponseCode() + ", responseMsg=" + this.getResponseMsg() + ", isSuccess=" + this.isSuccess() + ", data=" + this.getData() + ")";
             }
         }


        那么我們接口處理業(yè)務(wù)邏輯時代碼就會變成這樣,看起來非常多代碼:
         public ApiResult cancelService(@PathVariable Long serviceOrderId){
             ServiceOrder serviceOrder = serviceOrderMapper.selectByPrimaryKey(serviceOrderId);
             ApiResult result = new ApiResult<>();
             if (ObjectUtil.isNull(serviceOrder)) {
                 result.setSuccess(false);
                 result.setResponseCode(ErrorCodeEnum.FAIL.getCode());
                 result.setResponseMsg("查無此服務(wù)單");
                 return result;
             }
             if(serviceOrder.getOrderStatus().equals(cancelOrderStatus)){
                 result.setSuccess(false);
                 result.setResponseCode(ErrorCodeEnum.FAIL.getCode());
                 result.setResponseMsg("已取消的服務(wù)單不允許再次取消");
                 return result;
             }
             if(serviceOrder.getSortOrderId() != null){
                 result.setSuccess(false);
                 result.setResponseCode(ErrorCodeEnum.FAIL.getCode());
                 result.setResponseMsg("已配置物料的服務(wù)單不允許取消");
                 return result;
             }
             // ...other check
         
             // ...do something
             return result;
         }


        然后在上面這個代碼基礎(chǔ)上,我們可以觀察到,里面其實有非常多的重復(fù)代碼,完全可以把它們裝到 ApiResult 里面。


        這也是我看到很多開源框架的處理方式(PS:所以我第一個自己寫的框架也是這么處理的)


        在原 ApiResult 實體中增加一些公用的處理方法:
         public static ApiResult<String> success() {
             return success("success");
         }
         
         public static <T> ApiResult<T> success(T data{
             return (new ApiResult()).setResponseCode(0).setResponseMsg("操作成功").setSuccess(true).setData(data);
         }
         
         public static ApiResult<String> fail() {
             return fail(-1);
         }
         
         public static ApiResult<String> fail(int code{
             return fail(code, "fail");
         }
         
         public static <T> ApiResult<T> fail(T data{
             return fail(-1, data);
         }
         
         public static <T> ApiResult<T> fail(int code, T data{
             return (new ApiResult()).setResponseCode(code).setResponseMsg("操作失敗").setSuccess(false).setData(data);
         }
         
         public static <T> ApiResult<T> success(int code, String message, T data{
             return (new ApiResult()).setResponseCode(code).setResponseMsg(message).setSuccess(true).setData(data);
         }
         
         public static <T> ApiResult<T> fail(int code, String message, T data{
             return (new ApiResult()).setResponseCode(code).setResponseMsg(message).setSuccess(false).setData(data);
         }


        然后業(yè)務(wù)邏輯處理就變成這樣了,看起來還不錯是不是:
         /**
          * 取消服務(wù)單(不用斷言)
          */

         public ApiResult cancelService(Long serviceOrderId){
             ServiceOrder serviceOrder = serviceOrderMapper.selectByPrimaryKey(serviceOrderId);
             ApiResult result = new ApiResult<>();
             if (ObjectUtil.isNull(serviceOrder)) {
                 result = ApiResult.fail(ErrorCodeEnum.FAIL.getCode(), "查無此服務(wù)單");
                 return result;
             }
             if(serviceOrder.getOrderStatus().equals(cancelOrderStatus)){
                 result = ApiResult.fail(ErrorCodeEnum.FAIL.getCode(), "已取消的服務(wù)單不允許再次取消");
                 return result;
             }
             if(serviceOrder.getSortOrderId() != null){
                 result = ApiResult.fail(ErrorCodeEnum.FAIL.getCode(), "已配置物料的服務(wù)單不允許取消");
                 return result;
             }
             // ...other check
         
             // ...do something
             return result;
         }


        但是我們可以用異常處理類+斷言處理得更加簡化。


        增加異常處理類:
         @Slf4j
         @ControllerAdvice
         public class GlobalExceptionHandler {
             @ExceptionHandler(value = BusinessException.class)
             @ResponseBody
             public ResponseBean businessExceptionHandler(BusinessException e) {
                 log.info("business error : {}",e.getMessage(),e);
                 if (e.getCode() == -1) {
                     return ResponseBean.error(ApiCode.SERVICE_ERROR.getValue(), ApiCode.SERVICE_ERROR.getMessage());
                 }
                 return ResponseBean.error(e.getCode(), e.getMessage());
             }
         }


        增加異常類 BusinessException:
         /**
          * 業(yè)務(wù)異常,異常信息會返回到前端展示給用戶
          *
          * @date 2020/12/15 14:18
          */

         public class BusinessException extends RuntimeException {
             private static final long serialVersionUID = -5770538329754222306L;
         
             private int code = 1;
             private Level level;
         
             public BusinessException(int code, String message, Throwable cause) {
                 super(message, cause);
                 this.code = code;
             }
         
             public BusinessException(String message) {
                 super(message);
             }
         
             public BusinessException(Level level, String message) {
                 super(message);
                 this.level = level;
             }
         
             public BusinessException(Throwable cause) {
                 super(cause);
             }
         
             public BusinessException(int code, String message) {
                 super(message);
                 this.code = code;
             }
         
             public int getCode() {
                 return this.code;
             }
         
             public final Level getLevel() {
                 return this.level;
             }
         }


        增加斷言工具類 AssertUtil:
         public class AssertUtil extends cn.com.bluemoon.common.web.exception.AssertUtil  {
             public AssertUtil() {
             }
         
             /**
              * 服務(wù)調(diào)用異常
              * @param expression
              * @param message
              */

             public static void isTrueServiceInvoke(boolean expression, String message) {
                 if (!expression) {
                     throw new ServiceInvokeException(message);
                 }
             }
         
             /**
              * 拋出異常(默認(rèn)錯誤1000)
              * @param message
              */

             public static void businessInvalid(String message) {
                 throw new BusinessException(ApiCode.SERVICE_ERROR.getValue(), message);
             }
         
             /**
              * 表達(dá)式為真即拋出異常(默認(rèn)錯誤1000)
              *
              * @param expression
              * @param message
              */

             public static void businessInvalid(boolean expression, String message) {
                 if (expression) {
                     throw new BusinessException(ApiCode.SERVICE_ERROR.getValue(), message);
                 }
             }
         
             /**
              * 表達(dá)式為真即拋出異常
              *
              * @param expression
              * @param message
              */

             public static void businessInvalid(boolean expression, int code, String message) {
                 if (expression) {
                     throw new BusinessException(code, message);
                 }
             }
         }


        最后優(yōu)化的結(jié)果:
         /**
          * 取消服務(wù)單
          */

         public ApiResult cancelService(@PathVariable Long serviceOrderId){
             ServiceOrder serviceOrder = serviceOrderMapper.selectByPrimaryKey(serviceOrderId);
             AssertUtil.businessInvalid(ObjectUtil.isNull(serviceOrder),"查無此服務(wù)單");
             AssertUtil.businessInvalid(serviceOrder.getOrderStatus().equals(cancelOrderStatus),"查無此服務(wù)單");
             AssertUtil.businessInvalid(serviceOrder.getSortOrderId() != null,"查無此服務(wù)單");
             // ...other check
         
             // ...do something
             return ApiResult.success();
         }


        最后,我們可以看到我們的接口由 19 行的業(yè)務(wù)檢查代碼簡化到了 3 行。這只是單接口的情況下,在業(yè)務(wù)多且復(fù)雜的情況下能給我們節(jié)省更多的開發(fā)時間,把精力集中在核心業(yè)務(wù)上。


        附上代碼

        統(tǒng)一異常處理類:
         /**
          * 統(tǒng)一異常處理
          */

         @Slf4j
         @ControllerAdvice
         public class GlobalExceptionHandler {
         
             @ExceptionHandler(value = AssertException.class)
             @ResponseBody
             public ResponseBean bootExceptionHandler(AssertException e) {
                 ApiCode apiCode = ApiCode.getObjectByValue(e.getCode());
                 log.error("business error : {}", e.getMessage(), e);
                 if (e.getCode() == -1) {
                     return ResponseBean.error(ApiCode.SERVICE_ERROR.getValue(), ApiCode.SERVICE_ERROR.getMessage());
                 }
                 return ResponseBean.error(apiCode.getValue(), e.getMessage());
             }
         
             @ExceptionHandler(value = com.alibaba.fastjson.JSONException.class)
             public ResponseBean alibabaJsonExceptionHandler(com.alibaba.fastjson.JSONException e) {
                 ResponseBean response = new ResponseBean(false, ApiCode.PARAM_FORMAT_INCORR.getValue(), ApiCode.PARAM_FORMAT_INCORR.getMessage() + e.getMessage(), null);
                 log.error("1102", e);
                 return response;
             }
         
             @ExceptionHandler(value = JSONException.class)
             @ResponseBody
             public ResponseBean jsonExceptionHandler(JSONException e) {
                 ResponseBean response = new ResponseBean(false, ApiCode.PARAM_FORMAT_INCORR.getValue(), ApiCode.PARAM_FORMAT_INCORR.getMessage() + e.getMessage(), null);
                 log.error(ApiCode.PARAM_FORMAT_INCORR.getValue() + "", e);
                 return response;
             }
         
             @ExceptionHandler(value = JsonParseException.class)
             @ResponseBody
             public ResponseBean jsonParseExceptionHandler(JsonParseException e) {
                 ResponseBean response = new ResponseBean(false, ApiCode.PARAM_FORMAT_INCORR.getValue(), String.format(ApiCode.PARAM_FORMAT_INCORR.getMessage() + ":%s", e.getMessage()), null);
                 log.error(ApiCode.PARAM_FORMAT_INCORR.getValue() + "", e);
                 return response;
             }
         
             @ExceptionHandler(value = Exception.class)
             @ResponseBody
             public ResponseBean exceptionHandler(Exception e) {
                 ResponseBean response = new ResponseBean(false, ApiCode.SERVICE_ERROR.getValue(), ApiCode.SERVICE_ERROR.getMessage(), null);
                 log.error(ApiCode.SERVICE_ERROR.getValue() + "", e);
                 return response;
             }
         
             @ExceptionHandler(value = MethodArgumentTypeMismatchException.class)
             @ResponseBody
             public ResponseBean exceptionHandle(MethodArgumentTypeMismatchException e) {
                 ResponseBean response = new ResponseBean(false, ApiCode.PARAM_FORMAT_INCORR.getValue(), String.format(ApiCode.PARAM_FORMAT_INCORR.getMessage() + ":%s", e.getMessage()), null);
                 log.error(ApiCode.PARAM_FORMAT_INCORR.getValue() + "", e);
                 return response;
             }
         
             @ExceptionHandler(value = WebException.class)
             @ResponseBody
             public ResponseBean exceptionHandler(WebException e) {
                 ResponseBean response = new ResponseBean(e.getIsSuccess(), e.getResponseCode(), e.getResponseMsg(), null);
                 log.error(e.getResponseCode() + "", e);
                 return response;
             }
         
             @ExceptionHandler(value = IllegalArgumentException.class)
             @ResponseBody
             public ResponseBean exceptionHandler(IllegalArgumentException e) {
                 log.error("illegal request : {}", e.getMessage(), e);
                 return ResponseBean.error(ApiCode.PARAM_INVALID.getValue(), ApiCode.PARAM_INVALID.getMessage());
             }
         
             @ExceptionHandler(value = ServiceInvokeException.class)
             @ResponseBody
             public ResponseBean exceptionHandler(ServiceInvokeException e) {
                 log.error("serviceInvoke error request : {}", e.getMessage(), e);
                 return ResponseBean.error(ApiCode.SERVICE_ERROR.getValue(), ApiCode.SERVICE_ERROR.getMessage());
             }
         
             @ExceptionHandler(value = BusinessException.class)
             @ResponseBody
             public ResponseBean businessExceptionHandler(BusinessException e) {
                 log.info("business error : {}",e.getMessage(),e);
                 if (e.getCode() == -1) {
                     return ResponseBean.error(ApiCode.SERVICE_ERROR.getValue(), ApiCode.SERVICE_ERROR.getMessage());
                 }
                 return ResponseBean.error(e.getCode(), e.getMessage());
             }
         
             @ResponseBody
             @ExceptionHandler(MethodArgumentNotValidException.class)
             public ResponseBean  exceptionHandler(MethodArgumentNotValidException e) {
                 log.info("req params error", e);
                 String message = e.getBindingResult().getFieldError().getDefaultMessage();
                 if (StringUtils.isNotBlank(message) && !"不能為空".equals(message)) {
                     return ResponseBean.error(ApiCode.PARAM_INVALID.getValue(), message);
                 }
                 return ResponseBean.error(ApiCode.PARAM_INVALID.getValue(), ApiCode.PARAM_INVALID.getMessage());
             }
         
             @ExceptionHandler(value = TokenErrorException.class)
             @ResponseBody
             public ResponseBean tokenErrorExceptionHandler(TokenErrorException e) {
                 log.info("登錄失效 : {}",e.getMessage(),e);
                 return ResponseBean.error(ApiCode.SERVICE_ERROR.getValue(), "登錄已失效,請重新登錄!");
             }
         
             @ExceptionHandler(value = ServiceException.class)
             @ResponseBody
             public ResponseBean businessExceptionHandler(ServiceException e) {
                 log.info("service error : {}",e.getMessage(),e);
                 return ResponseBean.error(ApiCode.SERVICE_ERROR.getValue(), e.getMessage());
             }
         }


        異常情況枚舉,僅作參考:
         public enum ErrorCodeEnum implements EnumBase{
         
             FAIL(-1"網(wǎng)絡(luò)異常,請稍后再試"),
         
             SUCCESS(0"請求成功"),
         
             MAX_UPLOAD_SIZE_ERROR(1000"上傳文件不能超過20M"),
         
             SERVICE_BUSY_ERROR(1000"服務(wù)器正在繁忙,請稍后再試哦~"),
         
             REQUEST_PARAMS_FAIL(1001"參數(shù)錯誤"),
         
             USER_NOT_LOGIN(1002"用戶未登錄,請重新登錄"),
         
             USER_HAS_EXIST_LOGIN(1007"用戶已經(jīng)存在,請檢查!"),
         
             USER_CODE_NOT_EXIST(1008"用戶編碼不存在,請檢查!"),
         
             REQUEST_PARAMS_FORMAT_ERROR(1102"請求參數(shù)格式異常"),
         
             PASSWORD_SAFETY_ERROE(2204"密碼不符合安全規(guī)則,請通過忘記密碼重新設(shè)置8-18位數(shù)字+字母組合密碼"),
         
             TOKEN_EXPIRED(2301"token過期"),
         
             TOKEN_ERROR(2302"token驗證失敗"),
         
             INTERFACE_ERROR(10000"接口服務(wù)器異常");
         
             private final int code;
             private final String msg;
         
             ErrorCodeEnum(int code, String msg) {
                 this.code = code;
                 this.msg = msg;
             }
         
             @Override
             public int getCode() {
                 return this.code;
             }
         
             @Override
             public String getMsg() {
                 return this.msg;
             }
         }

        轉(zhuǎn)自:擼貓的代碼

        鏈接:https://juejin.cn/post/7111901850160332837

        -------------  END  -------------
        掃描下方二維碼,加入技術(shù)群。暗號:加群


        瀏覽 29
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            久久4 | 尤物视频官网 | 欧美特黄AA片 | 八重神子腿部功夫熟练 | 99久久综合 | 黄污涩涩啪啪www免费下载 | 美女扒开让男人桶爽 | 激情五月婷 | 中文字幕精品无码 | 山村大伦淫第1部分阅读小说 |