当前位置:网站首页>怎样巧用断言+异常处理类,使代码更简洁!(荣耀典藏版)
怎样巧用断言+异常处理类,使代码更简洁!(荣耀典藏版)
2022-07-28 19:50:00 【龍揹仩哋騎仕】

前言
大家好,我是程序缘--幻羽,我又来了!!
软件开发过程中,不可避免的是需要处理各种异常,所以代码中就会出现大量的 try {...} catch {...} finally {...} 代码块,不仅有大量的冗余代码,而且还影响代码的可读性。
另一个就是面对业务异常的情况,我们经常需要将业务异常结果组装成统一的信息返回给前端进行提示。
1、业务异常处理示例
假设我们定义的标准接口响应实体为 ApiResult:
@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:所以我第一个自己写的框架也是这么处理的)
在原 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);
}
然后业务逻辑处理就变成这样了,看起来还不错是不是:
/**
* 取消服务单(不用断言)
*/
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());
}
}
增加异常类 BusinessException:
/**
* 业务异常,异常信息会返回到前端展示给用户
*
* @date 2022/07/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() {
}
/**
* 服务调用异常
* @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 行的业务检查代码简化到了 3 行。这只是单接口的情况下,在业务多且复杂的情况下能给我们节省更多的开发时间,把精力集中在核心业务上。
2、附上代码
2.1统一异常处理类
/**
* 统一异常处理
*/
@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());
}
}
2.2.异常情况枚举,仅作参考
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, "密码不符合安全规则,请通过忘记密码重新设置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;
}
}总结
假如我们在每个接口中都去包装异常信息进行返回就会让代码变得很冗余且混乱。在我司的实际项目开发过程中。
希望本篇文章对你的编写代码可以带来帮助,会巧用断言去简化代码。
只有当你开始,你才会到达你的理想和目的地,只有当你努力,
你才会获得辉煌的成功,只有当你播种,你才会有所收获。只有追求,
才能尝到成功的味道,坚持在昨天叫立足,坚持在今天叫进取,坚持在明天叫成功。欢迎所有小伙伴们点赞+收藏!!!
都看到这里了,就点个 吧。
边栏推荐
- 华为发布首款电驱动系统DriveONE:充电10分钟续航200km
- 1945. 字符串转化后的各位数字之和
- Another installation artifact
- Baidu search is in line with expectations, but it involves the black hat strategy of the external chain. What is the reason?
- 【Bluetooth蓝牙开发】八、BLE协议之传输层
- Capture video by buffering
- MySQL 是如何归档数据的呢?
- 面向千元级5G手机市场,联发科天玑700发布
- Backup and recovery of SQL Server database
- SSM use @async and create threadpooltaskexecutor thread pool
猜你喜欢

CVPR 2022 | 网络中批处理归一化估计偏移的深入研究

High salary in the workplace | "intermediate and advanced test" interview questions

CVPR 2022 | in depth study of batch normalized estimation offset in network

Uncaught Error:Invalid geoJson format Cannot read property ‘length‘ of undefind

PyQt5快速开发与实战 5.4 网页交互

关键路径的分析

35 道 MySQL 面试必问题图解,这样也太好理解了吧

编码用这16个命名规则能让你少写一半以上的注释!

Top level "redis notes", cache avalanche + breakdown + penetration + cluster + distributed lock, Nb

大学荒废三年,大四自学7个月测试,找到了12K的工作
随机推荐
8、 QoS queue scheduling and message discarding
The Swedish court lifted the 5g spectrum auction ban on Huawei and ZTE
Backup and recovery of SQL Server database
DELTA热金属检测器维修V5G-JC-R1激光测量传感器/检测仪原理分析
瑞典法院取消对华为和中兴的5G频谱拍卖禁令
Summary of 29 typical problems in Devops planning and construction of securities enterprises based on containerized PAAS platform
CVPR 2022 | 网络中批处理归一化估计偏移的深入研究
(PMIC) full and half bridge drive csd95481rwj PDF specification
The framing efficiency of setpreviewcallbackwithbuffer will become lower
Explain C language 12 in detail (C language series)
国产芯片厂商助力,2020年白牌TWS耳机出货已达6亿部
牛客打开摄像头几秒后画面消失 | 相机打开画面一闪一闪
提前布局6G赛道!紫光展锐发布《6G无界 有AI》白皮书
Study - 几何计算总结
华为发布首款电驱动系统DriveONE:充电10分钟续航200km
SkiaSharp 之 WPF 自绘 拖曳小球(案例版)
Ijcai2022 tutorial | dialogue recommendation system
属性基加密仿真及代码实现(CP-ABE)论文:Ciphertext-Policy Attribute-Based Encryption
Invalid prompt object name in SQL Server
Attribute based encryption simulation and code implementation (cp-abe) paper: ciphertext policy attribute based encryption