# spring-boot-validation-demo **Repository Path**: luoex/spring-boot-validation-demo ## Basic Information - **Project Name**: spring-boot-validation-demo - **Description**: springboot validation框架集成示例 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2021-10-21 - **Last Updated**: 2023-06-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # spring-boot-validation-demo 提到输入参数的基本验证(非空、长度、大小、格式...),在以前我们还是通过手写代码,各种if、else、StringUtils.isEmpty、CollectionUtils.isEmpty...,真感觉快要疯了,太繁琐,Low爆了...,其实在Java生态提供了一套标准[JSR-380(aka. Bean Validation 2.0,part of Jakarta EE and JavaSE)](https://jcp.org/en/jsr/detail?id=380),它已成为`对象验证`事实上的标准,这套标准可以通过注解的形式(如@NotNull, @Size...)来对bean的属性进行验证。而`Hibernate Validator`对这套标准进行了实现,`SpringBoot Validation`无缝集成了Hibernate Validator、自定义验证器、自动验证的功能。下文将对SpringBoot集成Validation进行展开。 **注:** 完整示例代码可参见GitHub:[https://github.com/marqueeluo/spring-boot-validation-demo](https://github.com/marqueeluo/spring-boot-validation-demo) # constraints分类 JSR-380的支持的constrants注解汇总如下表:
分类 | 注解 | 适用对象 | null是否验证通过 | 说明 |
---|---|---|---|---|
非空 | @NotNull | 所有对象 | No | 不是null |
@NotEmpty | CharSequence, Collection, Map, Array | No | 不是null、不是""、size>0 | |
@NotBlank | CharSequence | No | 不是null、trim后长度大于0 | |
@Null | 所有对象 | Yes | 是null | |
长度 | @Size(min=0, max=Integer.MAX_VALUE) | CharSequence, Collection, Map, Array | Yes | 字符串长度、集合size |
大小 | @Positive | BigDecimal, BigInteger, byte, short, int, long, float, double | Yes | 数字>0 |
@PositiveOrZero | BigDecimal, BigInteger, byte, short, int, long, float, double | Yes | 数字>=0 | |
@Negative | BigDecimal, BigInteger, byte, short, int, long, float, double | Yes | 数字<0 | |
@NegativeOrZero | BigDecimal, BigInteger, byte, short, int, long, float, double | Yes | 数字<=0 | |
@Min(value=0L) | BigDecimal, BigInteger, byte, short, int, long | Yes | 数字>=min.value | |
@Max(value=0L) | BigDecimal, BigInteger, byte, short, int, long | Yes | 数字<=max.value | |
@Range(min=0L, max=Long.MAX_VALUE) | BigDecimal, BigInteger, byte, short, int, long | Yes | range.min<=数字<=range.max | |
@DecimalMin(value="") | BigDecimal, BigInteger, CharSequence, byte, short, int, long | Yes | 数字>=decimalMin.value | |
@DecimalMax(value="") | BigDecimal, BigInteger, CharSequence, byte, short, int, long | Yes | 数字<=decimalMax.value | |
日期 | @Past |
|
Yes | 时间在当前时间之前 |
@PastOrPresent | 同上 | Yes | 时间在当前时间之前 或者等于此时 | |
@Future | 同上 | Yes | 时间在当前时间之后 | |
@FutureOrPresent | 同上 | Yes | 时间在当前时间之后 或者等于此时 | |
格式 | @Pattern(regexp="", flags={}) | CharSequence | Yes | 匹配正则表达式 |
@Email @Email(regexp=".*", flags={}) |
CharSequence | Yes | 匹配邮箱格式 | |
@Digts(integer=0, fraction=0) | BigDecimal, BigInteger, CharSequence, byte, short, int, long | Yes | 必须是数字类型,且满足整数位数<=digits.integer, 浮点位数<=digits.fraction | |
布尔 | @AssertTrue | boolean | Yes | 必须是true |
@AssertFalse | boolean | Yes | 必须是false |
* Accepts {@code CharSequence}. {@code null} elements are considered valid.
*
* @author luo
* @date 2021-09-05
*/
@Documented
@Constraint(validatedBy = DateFormatValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD, ANNOTATION_TYPE,})
@Retention(RetentionPolicy.RUNTIME)
public @interface DateFormat {
String message() default "日期格式不正确";
String format() default "yyyy-MM-dd";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
}
import org.springframework.util.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.time.format.DateTimeFormatter;
/**
* Date Format validator
*
* @author luohq
* @date 2021-09-05
*/
public class DateFormatValidator implements ConstraintValidator
* Accepts {@code CharSequence}. {@code null} elements are considered valid.
*
* @author luo
* @date 2021-09-05
*/
@Documented
@Constraint(validatedBy = {})
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Repeatable(PhoneNo.List.class)
@ReportAsSingleViolation
@Pattern(regexp = "")
public @interface PhoneNo {
String message() default "电话号码格式不正确";
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
/**
* @return an additional regular expression the annotated PhoneNo must match. The default is "^\\d{8,11}$"
*/
@OverridesAttribute(constraint = Pattern.class, name = "regexp") String regexp() default "^\\d{8,11}$";
/**
* @return used in combination with {@link #regexp()} in order to specify a regular expression option
*/
@OverridesAttribute(constraint = Pattern.class, name = "flags") Pattern.Flag[] flags() default {};
/**
* Defines several {@code @URL} annotations on the same element.
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@interface List {
PhoneNo[] value();
}
}
```
**注:** 同理可以实现@IdNo约束
## 使用自定义constraints注解
可将之前的对象集成示例中代码调整为使用自定义验证注解如下:
```java
/**
* 用户 - DTO
*
* @author luohq
* @date 2021-09-04 13:45
*/
public class UserDto {
...
@NotBlank
//@Pattern(regexp = "^\\d{8,11}$")
@PhoneNo
private String phone;
@NotBlank
@IdNo
private String idNo;
@NotNull
//@Pattern(regexp = "^\\d{4}-\\d{2}-\\d{2}$")
@DateFormat
//@DateTimeFormat
private String birthDateStr;
...
}
```
同时自定义contrain还支持`跨多参数`、`验证对象里的多个field`、`验证返回对象`等用法,待后续再详细探索。
# 问题
通过在对象属性、方法参数上`标注注解`的形式,需要`侵入代码`,之前有的架构师不喜欢这种风格。
在一方开发时,我们有全部源码且在公司内部,这种方式还是可以的,且集成比较方便,
但是依赖三方Api jar包(参数对象定义在jar包中),我们无法直接去修改参数对象,依旧使用这种侵入代码的注解方式就不适用了,
针对三方包、或者替代注解这种形式,之前公司内部有实现过`基于xml配置`的形式进行验证,
这种方式不侵入参数对象,且集成也还算方便,
但是用起来还是没有直接在代码里写注解来的顺手(代码有补全、有提示、程序员友好),
**所以一方开发时,首选推荐SpringBoot Validation这套体系,无法直接编辑参数对象时再考虑其他方式。**
参考:
[【自定义validator - field、class level】https://www.baeldung.com/spring-mvc-custom-validator](https://www.baeldung.com/spring-mvc-custom-validator)
[【Spring boot集成validation、全局异常处理】https://www.baeldung.com/spring-boot-bean-validation](https://www.baeldung.com/spring-boot-bean-validation)
[【JSR380、非Spring框架集成validation】https://www.baeldung.com/javax-validation](https://www.baeldung.com/javax-validation)
[【方法约束 - Single param、Cross param、Return value自定义constraints、编程调用验证】https://www.baeldung.com/javax-validation-method-constraints](https://www.baeldung.com/javax-validation-method-constraints)
[Spring Validation最佳实践及其实现原理,参数校验没那么简单!](https://segmentfault.com/a/1190000023471742)
[https://reflectoring.io/bean-validation-with-spring-boot/](https://reflectoring.io/bean-validation-with-spring-boot/)