缘起
做SpringBoot项目时需要验证request参数,e.g:
这样的需求太常见了,几乎每个Controller接口都要验证,对于普通的校验规则如果手动校验,重复又繁琐,而且特别不优雅。试想,谁想在每个方法前都要排上一长串校验方法呢?
public String hello(@RequestParam("name") String name) throws Exception {
validateNotNull(name);
validateNotBlank(name);
...
// business logic
}
所以,一个常见的需求出现了:有没有可以自动校验Controller层请求参数的框架?
使得我们可以使用更加轻便的方式(例如注解)进行参数校验,而不用吭哧吭哧地手写校验逻辑。
答案是有的,Java设计者们和Java社区早就遇到了个问题,并且提出了解决方案。
这就是Bean Validation。
Bean Validation
Bean Validation规范在2009年就被提出,主要用来解决Java bean的验证问题,这在Bean Validation的Description中描述得很清楚:
This JSR will define a meta-data model and API for JavaBeanTM validation based on annotations, with overrides and extended meta-data through the use of XML validation descriptors.
可以看到,Bean Validation解决的问题比之前描述的场景更为普遍:不仅仅是Controller层参数,而是使用注解进行任意Java Bean的验证问题。
Bean Validation自提出至今已经历经了JSR303,JSR349和JSR380三个提案(详见:JSR:Bean Validation,有关JSR请读者自行了解,简单地说,就是Java官方给出的规范建议,但不提供实现)。
根据JSR,Java EE 提供了javax.validation.api(java EE改名为jakarta EE后,javax.validation.api也更名为jakarta.validation.api),而Apache Hibernate根据该api提供了hibernate-validator,平常我们说的Validate框架99.99%指的就是hibernate-validator。
简单捋一下,就是:
其中的Bean Validation JSR只提出规范,不涉及coding,Bean Validation API是Java官方根据JSR设计的API,并不提供实现。而hibernate-validator是Apache Hibernate基金会根据Bean Validation API提供的实现。
走进hibernate-validator
SpringMVC默认集成hibernate-validator
翻看我们的SpringBoot项目,好像没有专门import hibernate-validator,但是还是可以直接用@NotNull,@NotBlank等注解,难道真的没有引入吗?
其实排查起来很简单:
mvn dependency:tree
- Ctr + Shift + F
一般情况下,对于SpringBoot项目,默认集成了spring-boot-starter-validation,后者默认集成了hibernate-validator框架,不需要单独引入。当然你,在实测中发现这也和版本有关,有些版本的SpringBoot并没有集成,需要单独引入。
不管是自动继承还是手动引入,SpringBoot(SpringMVC)默认支持hibernate-validator框架。
hibernate-validator支持哪些校验规则
上文提到:
Java Validation自提出到现在也已经有十多年了,以最新的JSR380为例,从上往下捋下来是这样的:
JSR380 ——> Bean Validation 2.0 ——> hibernate-valiidatot 6.0 + 所以与其问hibernate-validator支持哪些校验规则不如问Bean Validation 2.0规定了哪些API。查看官方文档Built-in Constraint definitions可以看到,Bean Validation 2.0拥有22种“内建约束(built-in constraints)”:
8.1. @Null constraint
8.2. @NotNull constraint
8.3. @AssertTrue constraint
8.4. @AssertFalse constraint
8.5. @Min constraint
8.6. @Max constraint
8.7. @DecimalMin constraint
8.8. @DecimalMax constraint
8.9. @Negative constraint
8.10. @NegativeOrZero constraint
8.11. @Positive constraint
8.12. @PositiveOrZero constraint
8.13. @Size constraint
8.14. @Digits constraint
8.15. @Past constraint
8.16. @PastOrPresent constraint
8.17. @Future constraint
8.18. @FutureOrPresent constraint
8.19. @Pattern constraint
8.20. @NotEmpty constraint
8.21. @NotBlank constraint
8.22. @Email constraint
每一种约束都有说明。
当然,本地的hibernate-validator也可以查看每个约束文档,引入hibernate-validator包后,搜索任意一个约束如NotNull:
可以看到,22个约束(以注解形式出现)都定义在jakarta.validation-api(也就是java.validation-api)中,版本2.0.2,hibernate-validator就是实现该接口。
Examples
使用@Vadation开启校验(使所有constraints生效),其中的NacosConfig是个Java Bean:
其中的@Valid是个递归校验的注解,使用这个注解在属性上时,会递归地向下校验(有关@Valid请自行了解):
其中的match,talosConfig都使用了@Valid进行递归校验:
其中的serviceName和InstanceIPs使用了自定义注解:@ValidServiceName和@ValidIP。
- 如果对自定义@Validation注解感兴趣,请自行了解,可参考:Java Bean Validation自定义注解
- List<@ValidIP String>来源于Bean Validation 2.0增加的容器元素注解支持What’s new in 2.0:
Ps:如果是对容器中的每个元素进行递归校验,可以使用List<@Valid JavaBean>的方式。
注意,所有内建的constraints包括@Valid在校验的属性为null自动返回true。如果是自定义注解,也建议在isValid()方法第一行加上:
if(null == xxx) return true;
即统一风格,如果校验对象为null,则默认校验通过。
程序化校验
参见:Programmatic Validation 我写的example:
参考资料
参考:深入了解数据校验:Java Bean Validation 2.0(JSR380)