最近端午好久没有和二胖聚一聚了,于是约了二胖到人民广场去宰他一顿,正好最近他跳槽加薪了。
我:二胖听说你最近跳槽了,并且还是从传统软件公司跳到了互联网公司,工资是不是涨了一点啊,今天你请客哈。
二胖:别说了,工资是涨了点,但是性价比反而变低了,以前到点就下班,现在下班到家都快12点了。
我:新公司怎么样还适应吗?除了上班时间久点。
二胖 :哎,这个还真稍微有点不适应,这不是刚进去没啥事,leader
就给我安排了一个简单的用户保存功能,原来以前公司个把小时就做好了的功能,在这新公司硬是折腾了两三天,真是苦不堪言。我改了好几个版本最终 leader
才满意的点了点头。
review review leader leader leader leader
leader
看了看说到:“这次代码比上次好多了,功能基本没啥问题了,但是这一块代码是不是可以在优化下,这样写不是很优雅”
if(Objects.isNull(user)){
throw new IllegalArgumentException("用户不能为空");
}
if(StringUtils.isEmpty(user.getUserName())){
throw new IllegalArgumentException("用户名不能为空");
}
if(StringUtils.isEmpty(user.getUserName())){
throw new IllegalArgumentException("用户名不能为空");
}
if(StringUtils.isEmpty(user.getSex())){
throw new IllegalArgumentException("用户性别不能为空");
}
if(Objects.isNull(user.getUserDetail())){
throw new IllegalArgumentException("用户详细信息不能为空");
}
if(Objects.isNull(user.getUserDetail().getAddress())){
throw new IllegalArgumentException("用户地址不能为空");
}
if(!"M".equals(user.getSex()) && !"F".equals(user.getSex())){
throw new IllegalArgumentException("用户性别不合法");
}
二胖也是一阵郁闷,还是怀念以前的公司啊,功能实现就好,代码想怎么写就怎么写。互联网公司就是规矩多,写完代码还要写单测,还要监控一堆破事,活该这群人996.时间都花到这上面去了。抱怨该抱怨但是代码还得改啊。现在疫情期间好不容易找一个工作不能丢啊。二胖想到以前不是学过 aop
吗?再配合下自定义注解,这样代码就应该比较优雅了吧,说干就干。
首先自定义了一个注解因为要校验参数
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.METHOD})
public @interface ParameterValidator {
}
ParameterValidator
注解的方法。然后通过切面获取所有请求的参数,获取参数之后就解析参数上面的注解。配置切面啥的都比较简单,稍微复杂的就是反射解析参数了,因为要涉及到请求参数的嵌套结构。二胖习惯性的面向百度编程能 copy
别人的代码坚决不去自己写。百度出来的基本上都是单层结构,简单基本类型的对象,没有涉及到是嵌套、级联的类型的参数。最后在 github
( 全球最大的同性交友网站
)找了一圈也没有找到合适的。既然拿来主义没有结果那就只能哼次哼次的自己写了,幸好自己以前学过点反射的知识。花了一个小时通过递归调用写了个粗糙的版本,比较粗糙还有很多场景没有考虑进去。不过基本可以满足条件了部分代码如下: public static void checkField(Object object, Class<?> aClass) throws IllegalAccessException {
boolean primitive = isPrimitive(aClass);
if (primitive) {
return;
}
Field[] declaredFields = filterField(aClass.getDeclaredFields());
for (Field field : declaredFields) {
makeAccessible(field);
// 校验我们自定义注解
MyNotBlank fieldAnnotation = field.getAnnotation(MyNotBlank.class);
Object currentObject = field.get(object);
if (Objects.nonNull(fieldAnnotation)) {
if (StringUtils.isEmpty(currentObject)) {
throw new IllegalArgumentException(field.getName()+":"+fieldAnnotation.message());
}
}
if (!isJavaClass(field.getType())) {
// 递归调用,有级联参数时候
checkField(currentObject);
} else if (field.getType().isPrimitive()) {
} else if (field.getType().isAssignableFrom(List.class)) {
// 递归调用,解析list类型
getList(field, currentObject);
}
}
}
不过这个现在支持类型为基本类型和 String
、 List
的 后续如果参数类型是数组、或者 Map
等等还得去解析。这时候同事二狗从旁边走过,看到二胖这么认真的在敲代码。
二狗:二胖你又在写什么 bug
啊。
二胖:在自己造个轮子,写个通用的参数校验。
二狗:这个现在市面上不是已经有现成的方案了吗? jsr(Java Specification Requests)
可以去了解下哦。
二胖:好的我马上去查下资料。
jsr
我们就得先了解下什么是 JCP(Java Community Process)
? JCP(Java Community Process) 是一个开放的国际组织,主要由Java开发者以及被授权者组成,职能是发展和更新。
JSR又是个什么东东列?
它是指向JCP提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,(如果你觉得自己牛逼你也可以提交一个) 以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。
Bean Validation 顾名思义是对 java Bean 的校验,目前为止,Java 对 Bean 的校验有3个规范。
JSR-303 : Bean Validation
JSR 349 : Bean Validation 1.1
JSR 380 : Bean Validation 2.0
Hibernate Validator
是 Bean Validation
的参考实现 . Hibernate Validator
提供了 JSR 303
规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint
。
代码实现
2.3
以后的版本 spring-boot-starter-web
已经去除了这个依赖,需要手动引入 Hibernate-validator
依赖,详细内容见官网描述 <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
springboot
项目的话直接引入 <dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.17.Final</version>
</dependency>
代码演示:方法前面这个注解 @Valid
是必须的,否则不生效哦。
@PostMapping(value = "/save2")
@ResponseBody
public ResultViewModel save2(@Valid @RequestBody User user){
boolean saveUser = saveUser(user);
if (saveUser) {
return ResultViewModelUtil.success();
}
return ResultViewModelUtil.error();
}
实体类上标上需要校验的规则注解就好了。
//被注释的元素,值必须是一个字符串,不能为null,且调用trim()后,长度必须大于0
@NotBlank(message = "")
//被注释的元素,值不能为null,但可以为"空",用于基本数据类型的非空校验上,而且被其标注的字段可以使用 @size、@Max、@Min 等对字段数值进行大小的控制
@NotNull(message = "")
//被注释的的元素,值不能为null,且长度必须大于0,一般用在集合类上面
@NotEmpty(message = "")
//被注释的元素必须符合指定的正则表达式。
@Pattern(regexp = "", message = "")
//被注释的元素的大小必须在指定的范围内。
@Size(min =, max =)
//被注释的元素,值必须是一个数字,且值必须大于等于指定的最小值
@Min(value = long以内的值, message = "")
//被注释的元素,值必须是一个数字,且值必须小于等于指定的最大值
@Max(value = long以内的值, message = "")
//被注释的元素,值必须是一个数字,其值必须大于等于指定的最小值
@DecimalMin(value = 可以是小数, message = "")
//被注释的元素,值必须是一个数字,其值必须小于等于指定的最大值
@DecimalMax(value = 可以是小数, message = "")
//被注释的元素,值必须为null
@Null(message = "")
//被注释的元素必须是一个数字,其值必须在可接受的范围内
@Digits(integer =, fraction =)
//被注释的元素,值必须为true
@AssertTrue(message = "")
//被注释的元素,值必须为false
@AssertFalse(message = "")
//被注释的元素必须是一个过去的日期
@Past(message = "")
//被注释的元素必须是一个将来的日期
@Future(message = "")
//被注释的元素必须是电子邮件地址
@Email(regexp = "", message = "")
//被注释的元素必须在合适的范围内
@Range(min =, max =, message = "")
//被注释的字符串的大小必须在指定的范围内
@Length(min =, max =, message = "")
唯一需要注意的点就是如果是级联校验的话需要在最外层加上 @Valid
为什么需要在校验的上一次标上 @Valid
这个注解,里面的校验才会生效列?有知道的或者感兴趣的可以去看看源码给我留言哦。
然后在配置一个全局的异常捕获器就好了,由于篇幅原因代码就不贴了,代码上传到了 github
上。校验结果:
总结
Hibernate-Validator
还可以自定义注解实现。 userId userId validator
springboot
的、比如使用的是Jfinal框架(这个是个国产框架大多数人能都不知道)、或者 soa
调用参数校验的时候,这时候可以怎么使用列? 更多使用姿势大家感兴趣的可以去官网解锁哦,不过日常开发的以上介绍基本就可以满足需求了。
api Hibernate-Validator leader
项目地址:https://github.com/worit1/validator
参考:
http://docs.jboss.org/hibernate/validator/4.2/reference/zh-CN/html_single/#validator-gettingstarted( 官网中文版本贴心吧
) https://docs.jboss.org/hibernate/validator/6.1/reference/en-US/html_single/#validator-gettingstarted https://juejin.im/post/5dd8d44c518825734e4cda22 https://www.cnblogs.com/mr-yang-localhost/p/7812038.html
特别推荐一个分享架构+算法的优质内容,还没关注的小伙伴,可以长按关注一下:
长按订阅更多精彩▼
如有收获,点个在看,诚挚感谢