JCP、JSR简介
- JCP(Java Community Process,Java社区进程)是一个开放的国际组织,是使Java开发者参与定义Java特征和未来版本的正式过程
- JSR(Java Specification Requests,Java规范提案),JCP使用JSR作为正式规范文档,收集提议加入到Java体系中的规范和技术,任何人都能提交JSR
Bean Validation与Hibernate Validator
- 最新的Bean验证提议编号为
JSR380
,提出了Bean Validation 2.0
规范 Hibernate Validator 6.0.x.Final
就是参考Bean Validation 2.0
实现的
集成
- 注意:
Spring Boot 2.3.0
版本后spring-boot-starter-web
和spring-boot-starter-webflux
不再内置validation starter
,需手动引入spring-boot-starter-validation
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
校验注解
Jakarta Bean Validation/JSR提供的所有校验注解
-
@Null
必须为 null -
@NotNull
必须不为 null -
@NotEmpty
必须不为 null 或 空 -
@NotBlank
字符序列必须不为空,以及去空格后的长度必须大于 0。与@NotEmpty
的不同之处在于,此约束只能应用于字符。 -
@AssertTrue
必须为 true -
@AssertFalse
必须为 false -
@Digits(integer=, fraction=)
必须最多包含 integer 位整数和 fraction 位小数的数字 -
@Email
必须为有效的电子邮件地址。可选参数 regexp 和 flags 允许指定电子邮件必须匹配的附加正则表达式(包括正则表达式标志) -
@Min(value)
必须是一个数值,其值必须大于等于指定的最小值 -
@Max(value)
必须是一个数值,其值必须小于等于指定的最大值 -
@DecimalMin(value)
必须是一个数字,其值必须大于等于指定的最小值 -
@DecimalMax(value)
必须是一个数字,其值必须小于等于指定的最大值 -
@Size(max=, min=)
长度必须在指定的范围内(包括),数据类型支持字符,Collection,Map和数组 -
@Negative
必须为负数,不包含零 -
@NegativeOrZero
必须为负或零 -
@Positive
必须为正数,不包含零 -
@PositiveOrZero
必须为正或零 -
@Past
必须是一个过去的日期 -
@Future
必须是一个将来的日期 -
@FutureOrPresent
必须是现在或将来的日期 -
@PastOrPresent
必须是过去或现在的日期 -
@Pattern(regex=,flag=)
必须符合指定的正则表达式
Hibernate Validator提供的常用校验注解 -
@CreditCardNumber(ignoreNonDigitCharacters=)
检查是否通过了Luhn校验和测试。此验证旨在检查用户错误,而不是信用卡有效性!ignoreNonDigitCharacters是否忽略非数字字符,默认值为false -
@Currency(value=)
检查货币单位javax.money.MonetaryAmount是否为指定货币单位的一部分 -
@DurationMax(days=, hours=, minutes=, seconds=, millis=, nanos=, inclusive=)
检查java.time.Duration元素不大于指定范围。如果将inclusive标识位设置为true,则允许相等 -
@DurationMin(days=, hours=, minutes=, seconds=, millis=, nanos=, inclusive=)
检查java.time.Duration元素不少于指定范围。如果将inclusive标识位设置为true,则允许相等 -
@EAN
检查字符序列是有效的EAN条形码。类型默认值为EAN-13 -
@ISBN
检查字符序列是有效的ISBN国际标准书号。类型默认值为ISBN-13 -
@Length(min=, max=)
验证字符序列长度介于(包括)指定的最小值和最大值之间 -
@Range(min=, max=)
检查数字值是否介于(包括)指定的最小值和最大值之间 -
@SafeHtml(whitelistType= , additionalTags=, additionalTagsWithAttributes=, baseURI=)
检查HTML值是否包含潜在的恶意脚本片段,例如校验注解使用示例
public class XBoot { @Size(max = 16) @NotNull(message = "不能为空") private String username; @Pattern(regexp = "^[1][3,4,5,6,7,8,9][0-9]{9}$", message = "11位手机号格式不正确") @NotNull(message = "不能为空") private String mobile; @Email(message = "邮箱格式不正确") @NotNull(message = "不能为空") private String email; }
验证请求参数
验证请求体(RequestBody)
Controller:
@RestController @RequestMapping("/api") publicclass XBootController { @PostMapping("/xboot") public ResponseEntity getPerson(@RequestBody @Valid XBoot xboot, @Valid User user) { return ResponseEntity.ok().body(xboot); } }
验证请求参数(Path Variables 和 Request Parameters)
Controller或Service:
- 一定不要忘记在类上加上Validated注解,这个参数可以告诉 Spring 去校验方法参数,Service中的方法同理
@RestController @RequestMapping("/api") // 注意记得加上该注解 @Validated public class XBootController { @GetMapping("/{id}") public R<Integer> get(@PathVariable("id") @Valid @Max(value = 999, message = "ID超过指定范围") Integer id, @RequestParam @Valid @Email(message = "邮箱格式不正确") String email) { return ResponseEntity.ok().body(id); } }
使用组
- 某些场景下我们需要对不同类有不同的验证规则,例如新增时不需验证ID(系统生成)而修改时需验证,可使用
groups
指定验证生效的类 - 第一步创建两个接口
public interface A { } public interface B { }
- 第二步指定验证组并使用
@Null(groups = A.class) @NotNull(groups = B.class) private String username;
@Service @Validated public class XBootService { @Validated(A.class) public void validateA(@Valid XBoot xboot) { ... } @Validated(B.class) public void validateB(@Valid XBoot xboot) { ... } }
- 使用验证组的时候一定要小心,这是一种反模式,还会造成代码逻辑性变差
自定义Validator
示例:校验自定义时间格式如yyyy-MM-dd HH:mm:ss
- 第一步创建注解
@Target({FIELD}) @Retention(RUNTIME) @Documented @Constraint(validatedBy = {DateValidatorImpl.class}) public @interface DateValidator { /** * 必须的属性 */ String message() default "日期字符格式不匹配"; /** * 必须的属性 * 用于分组校验 */ Class[] groups() default {}; Class[] payload() default {}; /** * 非必须 接收用户校验的时间格式 */ String dateFormat() default "yyyy-MM-dd HH:mm:ss"; }
- 第二步实现ConstraintValidator接口,重写isValid方法
import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; public class DateValidatorImpl implements ConstraintValidator<DateValidator, String> { private String dateFormat; @Override public void initialize(DateValidator constraintAnnotation) { // 获取时间格式 this.dateFormat = constraintAnnotation.dateFormat(); } @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { if (StrUtil.isBlank(value)) { return true; } try { Date date = DateUtil.parse(value, dateFormat); return date != null; } catch (Exception e) { return false; } } }
@NotNull
与@Column(nullable = false)
- 在使用 JPA 操作数据的时候会经常碰到
@Column(nullable = false)
这种类型的约束,区别:
@NotNull
是 JSR 303 Bean验证注解,与数据库约束无关
@Column(nullable = false)
是JPA声明列为非空的方法- 总的来说就是即前者用于验证,而后者则用于指示数据库创建表的时候对表的约束