斷言+異常處理類,代碼更簡潔了
今日推薦 為什么很多 SpringBoot 開發(fā)者放棄了 Tomcat,選擇了 Undertow? 專業(yè)的 Redis 可視化管理工具,支持跨平臺,UI美呆了! 利用Java8新特征,重構傳統(tǒng)設計模式,你學會了嗎? 接口請求合并的3種技巧,性能直接爆表! 史上最全的 IDEA Debug 調試技巧(超詳細案例) 利用多線程批量拆分 List 導入數據庫,效率杠杠的
文章來源:https://c1n.cn/E6fZj
背景
業(yè)務異常處理示例
附上代碼
背景
軟件開發(fā)過程中,不可避免的是需要處理各種異常,所以代碼中就會出現大量的 try {...} catch {...} finally {...} 代碼塊,不僅有大量的冗余代碼,而且還影響代碼的可讀性。
另一個就是面對業(yè)務異常的情況,我們經常需要將業(yè)務異常結果組裝成統(tǒng)一的信息返回給前端進行提示。
假如我們在每個接口中都去包裝異常信息進行返回就會讓代碼變得很冗余且混亂。在我司的實際項目開發(fā)過程中,我們會巧用斷言去簡化代碼。
業(yè)務異常處理示例
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiResult<T> implements 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() + ")";
}
}
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("查無此服務單");
return result;
}
if(serviceOrder.getOrderStatus().equals(cancelOrderStatus)){
result.setSuccess(false);
result.setResponseCode(ErrorCodeEnum.FAIL.getCode());
result.setResponseMsg("已取消的服務單不允許再次取消");
return result;
}
if(serviceOrder.getSortOrderId() != null){
result.setSuccess(false);
result.setResponseCode(ErrorCodeEnum.FAIL.getCode());
result.setResponseMsg("已配置物料的服務單不允許取消");
return result;
}
// ...other check
// ...do something
return result;
}
然后在上面這個代碼基礎上,我們可以觀察到,里面其實有非常多的重復代碼,完全可以把它們裝到 ApiResult 里面。
這也是我看到很多開源框架的處理方式(PS:所以我第一個自己寫的框架也是這么處理的)
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);
}
/**
* 取消服務單(不用斷言)
*/
public ApiResult cancelService(Long serviceOrderId){
ServiceOrder serviceOrder = serviceOrderMapper.selectByPrimaryKey(serviceOrderId);
ApiResult result = new ApiResult<>();
if (ObjectUtil.isNull(serviceOrder)) {
result = ApiResult.fail(ErrorCodeEnum.FAIL.getCode(), "查無此服務單");
return result;
}
if(serviceOrder.getOrderStatus().equals(cancelOrderStatus)){
result = ApiResult.fail(ErrorCodeEnum.FAIL.getCode(), "已取消的服務單不允許再次取消");
return result;
}
if(serviceOrder.getSortOrderId() != null){
result = ApiResult.fail(ErrorCodeEnum.FAIL.getCode(), "已配置物料的服務單不允許取消");
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());
}
}
/**
* 業(yè)務異常,異常信息會返回到前端展示給用戶
*
* @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;
}
}
public class AssertUtil extends cn.com.bluemoon.common.web.exception.AssertUtil {
public AssertUtil() {
}
/**
* 服務調用異常
* @param expression
* @param message
*/
public static void isTrueServiceInvoke(boolean expression, String message) {
if (!expression) {
throw new ServiceInvokeException(message);
}
}
/**
* 拋出異常(默認錯誤1000)
* @param message
*/
public static void businessInvalid(String message) {
throw new BusinessException(ApiCode.SERVICE_ERROR.getValue(), message);
}
/**
* 表達式為真即拋出異常(默認錯誤1000)
*
* @param expression
* @param message
*/
public static void businessInvalid(boolean expression, String message) {
if (expression) {
throw new BusinessException(ApiCode.SERVICE_ERROR.getValue(), message);
}
}
/**
* 表達式為真即拋出異常
*
* @param expression
* @param message
*/
public static void businessInvalid(boolean expression, int code, String message) {
if (expression) {
throw new BusinessException(code, message);
}
}
}
/**
* 取消服務單
*/
public ApiResult cancelService(@PathVariable Long serviceOrderId){
ServiceOrder serviceOrder = serviceOrderMapper.selectByPrimaryKey(serviceOrderId);
AssertUtil.businessInvalid(ObjectUtil.isNull(serviceOrder),"查無此服務單");
AssertUtil.businessInvalid(serviceOrder.getOrderStatus().equals(cancelOrderStatus),"查無此服務單");
AssertUtil.businessInvalid(serviceOrder.getSortOrderId() != null,"查無此服務單");
// ...other check
// ...do something
return ApiResult.success();
}
最后,我們可以看到我們的接口由 19 行的業(yè)務檢查代碼簡化到了 3 行。這只是單接口的情況下,在業(yè)務多且復雜的情況下能給我們節(jié)省更多的開發(fā)時間,把精力集中在核心業(yè)務上。
附上代碼
/**
* 統(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, "網絡異常,請稍后再試"),
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, "密碼不符合安全規(guī)則,請通過忘記密碼重新設置8-18位數字+字母組合密碼"),
TOKEN_EXPIRED(2301, "token過期"),
TOKEN_ERROR(2302, "token驗證失敗"),
INTERFACE_ERROR(10000, "接口服務器異常");
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;
}
}
最后,給大家推薦一個我的知識星球,現在加入,前 100 名,只需要 25 元即可,非常優(yōu)惠。
評論
圖片
表情
