当前位置:网站首页>怎样巧用断言+异常处理类,使代码更简洁!(荣耀典藏版)
怎样巧用断言+异常处理类,使代码更简洁!(荣耀典藏版)
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;
}
}总结
假如我们在每个接口中都去包装异常信息进行返回就会让代码变得很冗余且混乱。在我司的实际项目开发过程中。
希望本篇文章对你的编写代码可以带来帮助,会巧用断言去简化代码。
只有当你开始,你才会到达你的理想和目的地,只有当你努力,
你才会获得辉煌的成功,只有当你播种,你才会有所收获。只有追求,
才能尝到成功的味道,坚持在昨天叫立足,坚持在今天叫进取,坚持在明天叫成功。欢迎所有小伙伴们点赞+收藏!!!
都看到这里了,就点个 吧。
边栏推荐
- Niuke turns on the camera and the picture disappears a few seconds later | the picture flashes when the camera is turned on
- MySQL 是如何归档数据的呢?
- 比UUID更快更安全NanoID到底是怎么实现的?(荣耀典藏版)
- 基于Xilinx的时序分析与约束
- Achieve waterfall effect
- Leetcode 19. delete the penultimate node of the linked list [knowledge points: speed pointer, recursion, stack]
- 职场高薪 |「中高级测试」面试题
- C#连接MySql数据库详细步骤
- LeetCode链表问题——面试题02.07.链表相交(一题一文学会链表)
- 到底为什么不建议使用SELECT * ?
猜你喜欢

顺序表的实现

Icml2022 | timing self-monitoring video transformer

职场高薪 |「中高级测试」面试题

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

证券企业基于容器化 PaaS 平台的 DevOps 规划建设 29 个典型问题总结

The ref value ‘xxx‘ will likely have changed by the time this effect function runs. If this ref......

Analysis of critical path

比UUID更快更安全NanoID到底是怎么实现的?(荣耀典藏版)

IJCAI2022教程 | 对话推荐系统

八、QOS队列调度与报文丢弃
随机推荐
How to understand data mesh
MySQL
What is the purpose of database read-write separation [easy to understand]
Two excellent software of my love cracking, batch search text, image and video image quality enhancement
High salary in the workplace | "intermediate and advanced test" interview questions
Talk about row storage and column storage of database
怎么理解数据网格(Data Mesh)
牛客打开摄像头几秒后画面消失 | 相机打开画面一闪一闪
Using El date picker to report errors in sub components
【Bluetooth蓝牙开发】八、BLE协议之传输层
Meta opens the project aria pilot dataset and will develop real-time 3D maps in the future
Invalid prompt object name in SQL Server
LeetCode链表问题——142.环形链表II(一题一文学会链表)
物联网技术栈之网关技术
Maxwell 一款简单易上手的实时抓取Mysql数据的软件
微星宝安工厂失火!官方回应:无人员受伤,产线不受影响!
uniapp的进度条自定义
Capture video by buffering
大学荒废三年,大四自学7个月测试,找到了12K的工作
1945. sum of digits after string conversion