關注我
,回復關鍵字「spring」,文章來源:https://c1n.cn/E6fZj
軟件開發過程中,不可避免的是需要處理各種異常,所以代碼中就會出現大量的 try {...} catch {...} finally {...} 代碼塊,不僅有大量的冗餘代碼,而且還影響代碼的可讀性。
另一個就是面對業務異常的情況,我們經常需要將業務異常結果組裝成統一的信息返回給前端進行提示。
假如我們在每個接口中都去包裝異常信息進行返回就會讓代碼變得很冗餘且混亂。在我司的實際項目開發過程中,我們會巧用斷言去簡化代碼。
假設我們定義的標準接口響應實體為 ApiResult:@Data@Builder@NoArgsConstructor@AllArgsConstructorpublicclassApiResult<T>implementsSerializable{privatestaticfinallongserialVersionUID=411731814484355577L;privateintresponseCode;privateStringresponseMsg;privatebooleanisSuccess;privateTdata;publicStringtoString(){return"ApiResult(responseCode="+this.getResponseCode()+",responseMsg="+this.getResponseMsg()+",isSuccess="+this.isSuccess()+",data="+this.getData()+")";}}那麼我們接口處理業務邏輯時代碼就會變成這樣,看起來非常多代碼:publicApiResultcancelService(@PathVariableLongserviceOrderId){ServiceOrderserviceOrder=serviceOrderMapper.selectByPrimaryKey(serviceOrderId);ApiResultresult=newApiResult<>();if(ObjectUtil.isNull(serviceOrder)){result.setSuccess(false);result.setResponseCode(ErrorCodeEnum.FAIL.getCode());result.setResponseMsg("查無此服務單");returnresult;}if(serviceOrder.getOrderStatus().equals(cancelOrderStatus)){result.setSuccess(false);result.setResponseCode(ErrorCodeEnum.FAIL.getCode());result.setResponseMsg("已取消的服務單不允許再次取消");returnresult;}if(serviceOrder.getSortOrderId()!=null){result.setSuccess(false);result.setResponseCode(ErrorCodeEnum.FAIL.getCode());result.setResponseMsg("已配置物料的服務單不允許取消");returnresult;}//...othercheck//...dosomethingreturnresult;}然後在上面這個代碼基礎上,我們可以觀察到,裡面其實有非常多的重複代碼,完全可以把它們裝到 ApiResult 裡面。
這也是我看到很多開源框架的處理方式(PS:所以我第一個自己寫的框架也是這麼處理的)
在原 ApiResult 實體中增加一些公用的處理方法:publicstaticApiResult<String>success(){returnsuccess("success");}publicstatic<T>ApiResult<T>success(Tdata){return(newApiResult()).setResponseCode(0).setResponseMsg("操作成功").setSuccess(true).setData(data);}publicstaticApiResult<String>fail(){returnfail(-1);}publicstaticApiResult<String>fail(intcode){returnfail(code,"fail");}publicstatic<T>ApiResult<T>fail(Tdata){returnfail(-1,data);}publicstatic<T>ApiResult<T>fail(intcode,Tdata){return(newApiResult()).setResponseCode(code).setResponseMsg("操作失敗").setSuccess(false).setData(data);}publicstatic<T>ApiResult<T>success(intcode,Stringmessage,Tdata){return(newApiResult()).setResponseCode(code).setResponseMsg(message).setSuccess(true).setData(data);}publicstatic<T>ApiResult<T>fail(intcode,Stringmessage,Tdata){return(newApiResult()).setResponseCode(code).setResponseMsg(message).setSuccess(false).setData(data);}然後業務邏輯處理就變成這樣了,看起來還不錯是不是:/***取消服務單(不用斷言)*/publicApiResultcancelService(LongserviceOrderId){ServiceOrderserviceOrder=serviceOrderMapper.selectByPrimaryKey(serviceOrderId);ApiResultresult=newApiResult<>();if(ObjectUtil.isNull(serviceOrder)){result=ApiResult.fail(ErrorCodeEnum.FAIL.getCode(),"查無此服務單");returnresult;}if(serviceOrder.getOrderStatus().equals(cancelOrderStatus)){result=ApiResult.fail(ErrorCodeEnum.FAIL.getCode(),"已取消的服務單不允許再次取消");returnresult;}if(serviceOrder.getSortOrderId()!=null){result=ApiResult.fail(ErrorCodeEnum.FAIL.getCode(),"已配置物料的服務單不允許取消");returnresult;}//...othercheck//...dosomethingreturnresult;}但是我們可以用異常處理類+斷言處理得更加簡化。
@Slf4j@ControllerAdvicepublicclassGlobalExceptionHandler{@ExceptionHandler(value=BusinessException.class)@ResponseBodypublicResponseBeanbusinessExceptionHandler(BusinessExceptione){log.info("businesserror:{}",e.getMessage(),e);if(e.getCode()==-1){returnResponseBean.error(ApiCode.SERVICE_ERROR.getValue(),ApiCode.SERVICE_ERROR.getMessage());}returnResponseBean.error(e.getCode(),e.getMessage());}}/***業務異常,異常信息會返回到前端展示給用戶**@date2020/12/1514:18*/publicclassBusinessExceptionextendsRuntimeException{privatestaticfinallongserialVersionUID=-5770538329754222306L;privateintcode=1;privateLevellevel;publicBusinessException(intcode,Stringmessage,Throwablecause){super(message,cause);this.code=code;}publicBusinessException(Stringmessage){super(message);}publicBusinessException(Levellevel,Stringmessage){super(message);this.level=level;}publicBusinessException(Throwablecause){super(cause);}publicBusinessException(intcode,Stringmessage){super(message);this.code=code;}publicintgetCode(){returnthis.code;}publicfinalLevelgetLevel(){returnthis.level;}}publicclassAssertUtilextendscn.com.bluemoon.common.web.exception.AssertUtil{publicAssertUtil(){}/***服務調用異常*@paramexpression*@parammessage*/publicstaticvoidisTrueServiceInvoke(booleanexpression,Stringmessage){if(!expression){thrownewServiceInvokeException(message);}}/***拋出異常(默認錯誤1000)*@parammessage*/publicstaticvoidbusinessInvalid(Stringmessage){thrownewBusinessException(ApiCode.SERVICE_ERROR.getValue(),message);}/***表達式為真即拋出異常(默認錯誤1000)**@paramexpression*@parammessage*/publicstaticvoidbusinessInvalid(booleanexpression,Stringmessage){if(expression){thrownewBusinessException(ApiCode.SERVICE_ERROR.getValue(),message);}}/***表達式為真即拋出異常**@paramexpression*@parammessage*/publicstaticvoidbusinessInvalid(booleanexpression,intcode,Stringmessage){if(expression){thrownewBusinessException(code,message);}}}/***取消服務單*/publicApiResultcancelService(@PathVariableLongserviceOrderId){ServiceOrderserviceOrder=serviceOrderMapper.selectByPrimaryKey(serviceOrderId);AssertUtil.businessInvalid(ObjectUtil.isNull(serviceOrder),"查無此服務單");AssertUtil.businessInvalid(serviceOrder.getOrderStatus().equals(cancelOrderStatus),"查無此服務單");AssertUtil.businessInvalid(serviceOrder.getSortOrderId()!=null,"查無此服務單");//...othercheck//...dosomethingreturnApiResult.success();}最後,我們可以看到我們的接口由 19 行的業務檢查代碼簡化到了 3 行。這只是單接口的情況下,在業務多且複雜的情況下能給我們節省更多的開發時間,把精力集中在核心業務上。
/***統一異常處理*/@Slf4j@ControllerAdvicepublicclassGlobalExceptionHandler{@ExceptionHandler(value=AssertException.class)@ResponseBodypublicResponseBeanbootExceptionHandler(AssertExceptione){ApiCodeapiCode=ApiCode.getObjectByValue(e.getCode());log.error("businesserror:{}",e.getMessage(),e);if(e.getCode()==-1){returnResponseBean.error(ApiCode.SERVICE_ERROR.getValue(),ApiCode.SERVICE_ERROR.getMessage());}returnResponseBean.error(apiCode.getValue(),e.getMessage());}@ExceptionHandler(value=com.alibaba.fastjson.JSONException.class)publicResponseBeanalibabaJsonExceptionHandler(com.alibaba.fastjson.JSONExceptione){ResponseBeanresponse=newResponseBean(false,ApiCode.PARAM_FORMAT_INCORR.getValue(),ApiCode.PARAM_FORMAT_INCORR.getMessage()+e.getMessage(),null);log.error("1102",e);returnresponse;}@ExceptionHandler(value=JSONException.class)@ResponseBodypublicResponseBeanjsonExceptionHandler(JSONExceptione){ResponseBeanresponse=newResponseBean(false,ApiCode.PARAM_FORMAT_INCORR.getValue(),ApiCode.PARAM_FORMAT_INCORR.getMessage()+e.getMessage(),null);log.error(ApiCode.PARAM_FORMAT_INCORR.getValue()+"",e);returnresponse;}@ExceptionHandler(value=JsonParseException.class)@ResponseBodypublicResponseBeanjsonParseExceptionHandler(JsonParseExceptione){ResponseBeanresponse=newResponseBean(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);returnresponse;}@ExceptionHandler(value=Exception.class)@ResponseBodypublicResponseBeanexceptionHandler(Exceptione){ResponseBeanresponse=newResponseBean(false,ApiCode.SERVICE_ERROR.getValue(),ApiCode.SERVICE_ERROR.getMessage(),null);log.error(ApiCode.SERVICE_ERROR.getValue()+"",e);returnresponse;}@ExceptionHandler(value=MethodArgumentTypeMismatchException.class)@ResponseBodypublicResponseBeanexceptionHandle(MethodArgumentTypeMismatchExceptione){ResponseBeanresponse=newResponseBean(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);returnresponse;}@ExceptionHandler(value=WebException.class)@ResponseBodypublicResponseBeanexceptionHandler(WebExceptione){ResponseBeanresponse=newResponseBean(e.getIsSuccess(),e.getResponseCode(),e.getResponseMsg(),null);log.error(e.getResponseCode()+"",e);returnresponse;}@ExceptionHandler(value=IllegalArgumentException.class)@ResponseBodypublicResponseBeanexceptionHandler(IllegalArgumentExceptione){log.error("illegalrequest:{}",e.getMessage(),e);returnResponseBean.error(ApiCode.PARAM_INVALID.getValue(),ApiCode.PARAM_INVALID.getMessage());}@ExceptionHandler(value=ServiceInvokeException.class)@ResponseBodypublicResponseBeanexceptionHandler(ServiceInvokeExceptione){log.error("serviceInvokeerrorrequest:{}",e.getMessage(),e);returnResponseBean.error(ApiCode.SERVICE_ERROR.getValue(),ApiCode.SERVICE_ERROR.getMessage());}@ExceptionHandler(value=BusinessException.class)@ResponseBodypublicResponseBeanbusinessExceptionHandler(BusinessExceptione){log.info("businesserror:{}",e.getMessage(),e);if(e.getCode()==-1){returnResponseBean.error(ApiCode.SERVICE_ERROR.getValue(),ApiCode.SERVICE_ERROR.getMessage());}returnResponseBean.error(e.getCode(),e.getMessage());}@ResponseBody@ExceptionHandler(MethodArgumentNotValidException.class)publicResponseBeanexceptionHandler(MethodArgumentNotValidExceptione){log.info("reqparamserror",e);Stringmessage=e.getBindingResult().getFieldError().getDefaultMessage();if(StringUtils.isNotBlank(message)&&!"不能為空".equals(message)){returnResponseBean.error(ApiCode.PARAM_INVALID.getValue(),message);}returnResponseBean.error(ApiCode.PARAM_INVALID.getValue(),ApiCode.PARAM_INVALID.getMessage());}@ExceptionHandler(value=TokenErrorException.class)@ResponseBodypublicResponseBeantokenErrorExceptionHandler(TokenErrorExceptione){log.info("登錄失效:{}",e.getMessage(),e);returnResponseBean.error(ApiCode.SERVICE_ERROR.getValue(),"登錄已失效,請重新登錄!");}@ExceptionHandler(value=ServiceException.class)@ResponseBodypublicResponseBeanbusinessExceptionHandler(ServiceExceptione){log.info("serviceerror:{}",e.getMessage(),e);returnResponseBean.error(ApiCode.SERVICE_ERROR.getValue(),e.getMessage());}}publicenumErrorCodeEnumimplementsEnumBase{FAIL(-1,"網絡異常,請稍後再試"),SUCCESS(0,"請求成功"),MAX_UPLOAD_SIZE_ERROR(1000,"上傳文件不能超過20M"),SERVICE_BUSY_ERROR(1000,"服務器正在繁忙,請稍後再試哦~"),REQUEST_PARAMS_FAIL(1001,"參數錯誤"),USER_NOT_LOGIN(1002,"用戶未登錄,請重新登錄"),USER_HAS_EXIST_LOGIN(1007,"用戶已經存在,請檢查!"),USER_CODE_NOT_EXIST(1008,"用戶編碼不存在,請檢查!"),REQUEST_PARAMS_FORMAT_ERROR(1102,"請求參數格式異常"),PASSWORD_SAFETY_ERROE(2204,"密碼不符合安全規則,請通過忘記密碼重新設置8-18位數字+字母組合密碼"),TOKEN_EXPIRED(2301,"token過期"),TOKEN_ERROR(2302,"token驗證失敗"),INTERFACE_ERROR(10000,"接口服務器異常");privatefinalintcode;privatefinalStringmsg;ErrorCodeEnum(intcode,Stringmsg){this.code=code;this.msg=msg;}@OverridepublicintgetCode(){returnthis.code;}@OverridepublicStringgetMsg(){returnthis.msg;}}
大公司為什麼禁止SpringBoot項目使用Tomcat?Spring Cloud Stream 使用延遲消息實現定時任務(RabbitMQ)Java 8的Stream不好調試?那是你姿勢不對!重磅!VS Code 6月更新:Spring Boot功能重大升級!Elasticsearch 實現分頁的 3 種方式,還有誰不會??
關注後端面試那些事,回復【2022面經】
獲取最新大廠Java面經
最後重要提示:高質量的技術交流群,限時免費開放,今年抱團最重要。想進群的,關注SpringForAll社區
,回復關鍵詞:加群,拉你進群。