突然想起以前一个同事刚入项目的时候干过的一件有趣的事,入项分配给他的第一个开发任务,在项目里引入了几十个util工具类,据说是多年珍藏,有的还是自己写的,然后上灰度的时候发现各种的maven冲突,相信有很新手都干过类似的事吧。大部分的项目基本是ssh的框架,像一些断言、字符串、对象、集合、文件相关的工具类不可能还需要自己来封装,为什么呢?Spring家族生态这么茂盛,Spring以及其他一些框架技术内部的肯定少不了会用到断言、对象、集合、文件读取相关的操作方法,Spring都封装的这么好,这些小的内容大概率自己也会封装好。去找找源码,一看还真有。既然有,那就拿来用呗,基本不用担心有bug。当然如果不适合自己的需要,可以继承再重写呗,至少不用自己逞能搞了一堆,可能还有bug。后面陆续用几篇文章,一起学习这些工具类的用法。
断言是一个很普通的东西,用于数据的合法性检查,如果经常去看Spring的源码的话,会经常看到。举个例子:查询员工信息详情的时候,至少要传员工id或编号之类的一个唯一性标识,如果不传就抛出异常;保存员工信息的时候,要求员工编号一定不能为空;更新员工信息时,员工id一定不能为空;遍历员工信息集合时,集合一定不能为空,类似这些场景都可以用到断言。
但是实际的业务开发中,我发现很多人喜欢这样写:
@GetMapping("/info")
public ResResult employee(Integer id){
if (null==id) {
return ResResult.fail("查询员工详情时,id不能为空");
}
//查询员工详情然....
return ResResult.success("");
}
其实这样写也不是不对,是不太优雅,其实可以用断言来写看起来会更好,编写Spring、mybaits的大佬们都是这样写的,所以经常看源码的另外一个好处就是可以向大神学习如何优雅的写代码:
用伪代码演示一下isNull的用法,正常情况下对外暴露查询员工基本信息时,一些敏感字段是不允许输出的,如果有类似的场景,可以这样写:
@PostMapping("/baseInfo")
@PostMapping("/baseInfo")
public Employee baseInfo(@RequestBody Employee employee){
//查询员工基本信息
Employee result=new Employee();
result.setRealName("zhangsan");
result.setSex("男");
result.setAddress("北京市东直门八大碗胡同11号");
result.setPhone("155032718xx");
result.setEmail("[email protected]");
Assert.isNull(result.getAddress(), "员工家庭地址是敏感信息,输出基本信息时不能包含此字段");
Assert.isNull(result.getPhone(), "员工手机号码是敏感信息,输出基本信息时不能包含此字段");
Assert.isNull(result.getEmail(), "员工邮箱地址是敏感信息,输出基本信息时不能包含此字段");
return result;
}
如果对外输出的员工基本信息的结果中,没有包含地址、手机号码、邮箱地址,则正常通过断言;如果包含有这些敏感信息,则为抛出异常信息并携带定制的异常提示;
在如保存员工信息等类似场景时,会要求某些字段不能空,这个时候可以这样写:
@PostMapping("/save")
public Employee save(@RequestBody Employee employee){
Assert.notNull(employee.getEmpNo(), "保存员工信息时,请先给员工分配员工编号");
//保存入库逻辑
return employee;
}
如果保存员工信息时未给员工分配员工编号,则不能通过断言,并抛出异常,终止业务程序执行;如果保存员工信息时已经分配过员工编号,则通过断言继续向下执行;
有时候也会遇到一些逻辑真判断,如:更新员工信息前,要先确认待更新员工是否存在,可以这样写:
@PostMapping("/update")
public Employee update(@RequestBody Employee paramObj){
//查询员工是否存在,结果为false
boolean isExist=false;
Assert.isTrue(isExist, "待更新员工信息不存在,更新失败");
return paramObj;
}
在对一些集合类参数校验非空校验的时候,可以使用Assert.notEmpty,如在遍历集合前需要校验集合不null并且有元素的时候:
public void iterEmployeeList(){
List list = new ArrayList<>();
Assert.notEmpty(list, "员工信息集合为null或集合内无任何元素,遍历失败");
for (Employee employee : list) {
System.out.println(employee.getRealName());
}
}
如果遍历的集合是null或集合内没有任何元素,则不能通过断言,终止程序执行并抛出异常;如果遍历集合至少有一个元素,则继续执行遍历操作;
可以这么理解断言:在输出结果后,对结果集定义一个期望值,如果满足了期望值,则正常通过断言;如果不满足期望值,则会触发断言,触发断言的结果就是会抛出一个IllegalArgumentException异常,这个异常属于运行时异常。最后,再搞一个异常的统一处理,简直不要太完美。
@RestControllerAdvice
public class CommonExceptionHandler {
@ExceptionHandler(value = IllegalArgumentException.class)
public String illegalArgumentException(IllegalArgumentException e){
//统一异常处理可以捕获到该异常,并统一处理
String message = e.getMessage();
return message;
}
}
另外断言肯定不止这几个,这里只是对用到频率比较高的作一个抛砖引玉,更多更好玩的断言使用可以从org.springframework.util.Assert类探索一翻。
Springboot扩展点系列实现方式、工作原理集合:
Springboot扩展点之ApplicationContextInitializer
Springboot扩展点之BeanDefinitionRegistryPostProcessor
Springboot扩展点之BeanFactoryPostProcessor
Springboot扩展点之BeanPostProcessor
Springboot扩展点之InstantiationAwareBeanPostProcessor
Springboot扩展点之SmartInstantiationAwareBeanPostProcessor
Springboot扩展点之ApplicationContextAwareProcessor
Springboot扩展点之@PostConstruct
Springboot扩展点之InitializingBean
Springboot扩展点之DisposableBean
Springboot扩展点之SmartInitializingSingleton
Springboot核心功能工作原理:
Springboot实现调度任务的工作原理
Springboot事件监听机制的工作原理