1、为什么要这么做
在上一篇最后提到就是面对业务代码中出现异常的情况,如果不处理,则展示给用户的信息是非常不友好的。比如以下代码出现空指针时
那么前端得到的响应是啥样?
此外在业务代码中可能会用很多的校验,有的校验返回一些提示信息,有的校验可能需要抛出指定的异常。
通常我们使用try {...} catch {...} finally {...} 代码块来处理异常,而这些异常我们是不能直接展现给用户。所以对异常进行分类统一处理,减少冗余代码,使代码风格统一更优雅。
2、断言处理
像上图中一个代码块中会有很多的校验,而实际项目中非空或者一些业务判断都是很频繁的。我们来改造一下上图中的代码,使用断言后两行代码就解决了
到这里不用看源码大家肯定也已经猜到了,它肯定用的if(){}
是null就抛出异常,而state()却不一样,为false时抛出异常,所以这里的表达式填我们想要的结果。
用之前去Assert类中看一看,多用几次自然就熟练了,开发起来也会更加的快速。
实际项目中我们可能还需要调用其他服务暴露出来的接口,调用失败后抛出自定义的异常
此时我们也可以写个断言工具类
publicclassAssertUtil { /*** 服务调用异常* @param expression* @param message*/publicstaticvoidisTrueServiceInvoke(booleanexpression, Stringmessage) { if (!expression) { thrownewServiceInvokeException(message); } } }
那么这一类问题,都可以使用,解决了代码冗余问题
3、统一异常处理
通过以上描述我们也可以看出,项目中除了空指针这种异常,还有断言抛出的异常,还有自定义异常,对各种各样的异常统一处理,使得返回更加友好的信息。
主要通过 @RestControllerAdive
提升作用域,通过 @ExceptionHandler
注解来处理不同的异常。
importcom.example.assertdemo.common.exception.ServiceInvokeException; importcom.example.assertdemo.constant.ResultCodeEnum; importlombok.extern.slf4j.Slf4j; importorg.springframework.web.bind.annotation.ControllerAdvice; importorg.springframework.web.bind.annotation.ExceptionHandler; importorg.springframework.web.bind.annotation.ResponseBody; publicclassGlobalExceptionHandler { NullPointerException.class) (publicApiResultnullPointerExceptionHandler(NullPointerExceptionexception) { log.error(exception.getMessage()); returnApiResult.fail(ResultCodeEnum.SERVE_EXCEPTION); } ServiceInvokeException.class) (publicApiResultserviceInvokeExceptionHandler(ServiceInvokeExceptionexception){ log.error(exception.getMessage()); returnApiResult.fail(ResultCodeEnum.SERVE_EXCEPTION); } }
返回的状态码和提示信息搞个枚举类
publicenumResultCodeEnum{ /*** success*/SUCCESS(0,"操作成功"), /*** fail*/FAIL(-1,"操作失败"), /*** 参数错误:1001-1999*/PARAM_IS_INVALID(1001,"参数无效"), PARAM_TYPE_ERROR(1002,"参数类型错误"), /*** 业务错误:2001-2999*/TERMINATE_CONTRACT_FAIL(2001,"终止合同失败,请联系管理员"), SERVE_EXCEPTION(3001,"当前服务出小差了,请联系管理员"), ; /*** 状态码*/privatefinalintcode; /*** 提示信息*/privatefinalStringmessage; ResultCodeEnum(Integercode, Stringmessage){ this.code=code; this.message=message; } }
此时我们再去请求2、断言处理中的接口,这种提示就非常友好,不会给用户展示看不懂的一连串异常信息,还能让开发者及时定位到问题去处理。
4、断言类中的方法
说白了很多方法就是填入你希望的结果,比如isTrue(a==1,""),也就是我希望a等于1,如果不等于就抛出异常。
对象和类型断言
方法 |
说明 |
notNull() |
对象是null抛出异常 |
isNull() |
对象不是null抛出异常 |
isInstanceOf() |
检查对象必须为另一个特定类型的实例 |
isAssignable() |
检查类型 |
文本断言
方法 |
说明 |
hasLength() |
只要不是null和空字符串就不会报异常 |
hasText() |
增强检查条件,字符串至少包含一个非空白字符,可以使用hasText()方法 |
doesNotContain() |
检查参数不包含特定子串 |
逻辑断言
方法 |
说明 |
isTrue() |
条件为假抛出IllegalArgumentException 异常 |
state() |
该方法与isTrue一样,但抛出IllegalStateException异常 |
Collection和map断言
方法 |
说明 |
Collection应用notEmpty() |
Collection不是null并包含至少一个元素 |
map应用notEmpty() |
检查map不null,并至少包含一个entry(key,value键值对) |
数组断言
方法 |
说明 |
notEmpty() |
可以检查数组不null,且至少包括一个元素 |
noNullElements() |
确保数组不包含null元素 |