Bean Validation
Bean Validation 只是规范,不提供具体的实现。
2009 年发布了 Bean Validation1.0 (JSR 303)规范,随后历经 Bean Validation 1.1 (JSR 349) ,Bean Validation 2.0 (JSR 380) , Jakarta Bean Validation 2.0
Spring Validation
源码地址 https://github.com/spring-projects/spring-framework/tree/main/spring-context/src/main/java/org/springframework/validation
maven依赖仓库
https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-validation
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
校验注解
判空
@NotBlank | 只能用于字符串不为 null ,并且字符串 #trim() 以后 length 要大于 0 |
---|---|
@NotEmpty | 集合对象的元素不为 0 ,即集合不为空,也可以用于字符串不为 null 。 |
@NotNull | 不能为 null 。 |
@Null | 必须为 null |
- 数值检查
- @DecimalMax(value) :被注释的元素必须是一个数字,其值必须小于等于指定的最大值。
- @DecimalMin(value) :被注释的元素必须是一个数字,其值必须大于等于指定的最小值。
- @Digits(integer, fraction) :被注释的元素必须是一个数字,其值必须在可接受的范围内。
- @Positive :判断正数。
- @PositiveOrZero :判断正数或 0 。
- @Max(value) :该字段的值只能小于或等于该值。
- @Min(value) :该字段的值只能大于或等于该值。
- @Negative :判断负数。
- @NegativeOrZero :判断负数或 0 。
- Boolean 值检查
- @AssertFalse :被注释的元素必须为 true 。
- @AssertTrue :被注释的元素必须为 false 。
- 长度检查
- @Size(max, min) :检查该字段的 size 是否在 min 和 max 之间,可以是字符串、数组、集合、Map 等。
- 日期检查
- @Future :被注释的元素必须是一个将来的日期。
- @FutureOrPresent :判断日期是否是将来或现在日期。
- @Past :检查该字段的日期是在过去。
- @PastOrPresent :判断日期是否是过去或现在日期。
- 其它检查
- @Email :被注释的元素必须是电子邮箱地址。
- @Pattern(value) :被注释的元素必须符合指定的正则表达式。 ```java 注解 @Null 被注释的元素必须为 null @NotNull 被注释的元素必须不为 null @AssertTrue 被注释的元素必须为 true @AssertFalse 被注释的元素必须为 false @Min(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 @Max(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 @DecimalMin(value) 被注释的元素必须是一个数字,其值必须大于等于指定的最小值 @DecimalMax(value) 被注释的元素必须是一个数字,其值必须小于等于指定的最大值 @Size(max=, min=) 被注释的元素的大小必须在指定的范围内 @Digits (integer, fraction) 被注释的元素必须是一个数字,其值必须在可接受的范围内 @Past 被注释的元素必须是一个过去的日期 @Future 被注释的元素必须是一个将来的日期 @Pattern(regex=,flag=) 被注释的元素必须符合指定的正则表达式
eg
```java
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
public class User {
private Long id;
@NotNull(message = "用户账号不能为空")
@Size(min = 6, max = 11, message = "账号长度必须是6-11个字符")
private String username;
@NotNull(message = "用户密码不能为空")
@Size(min = 6, max = 8, message = "密码长度必须是6-8个字符")
private String password;
@NotNull(message = "用户邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
// 不允许为空,并且年龄的最小值为18
@NotNull
@Min(18)
private Integer age;
}
Hibernate Validator
github :https://github.com/hibernate/hibernate-validator
@NotBlank(message =) 验证字符串非null,且长度必须大于0
@Email 被注释的元素必须是电子邮箱地址
@Length(min=,max=) 被注释的字符串的大小必须在指定的范围内
@NotEmpty 被注释的字符串的必须非空
@Range(min=,max=,message=) 被注释的元素必须在合适的范围内
@Validated @Valid
- @Valid:javax.validation 提供,作用在方法,构造方法,参数,成员属性上。
@Valid 可做嵌套校验,作用在属性上,属性是一个 Java Bean,需要对属性 Bean 的内部属性进行校验。
@PostMapping("/validation")
public String addUser(@RequestBody @Valid User user, BindingResult bindingResult) {
// 如果有参数校验失败,返回错误信息
if (bindingResult.hasErrors()) {
System.out.println(user.toString());
System.out.println(bindingResult.getErrorCount());
System.out.println(bindingResult.getAllErrors());
}
for (ObjectError error : bindingResult.getAllErrors()) {
return error.getDefaultMessage();
}
return user.toString();
}
总的来说,绝大多数场景下,我们使用 @Validated 注解即可。
而在有嵌套校验的场景,我们使用 @Valid 注解添加到成员属性上。自定义约束
自定义定义约束主要包括以下2个步骤:
1)编写自定义约束的注解;
2)编写自定义的校验器 ConstraintValidator 。
因此自定义约束由两部分组成:校验注解和校验器校验注解
校验注解:就一个普通的 Java 注解,但多个了元注解 @Constraint 用于指定该注解的校验器。validatedBy 的值指定为校验逻辑的实现类,即校验器。
@Constraint(validatedBy = {xxxxx.class})
@Retention :用来说明该注解类的生命周期。它有以下三个参数:
RetentionPolicy.SOURCE : 注解只保留在源文件中
RetentionPolicy.CLASS : 注解保留在class文件中,在加载到JVM虚拟机时丢弃
RetentionPolicy.RUNTIME : 注解保留在程序运行期间,此时可以通过反射获得定义在某个类上的所有注解。
@Target : 用来说明该注解可以被声明在那些元素之前。
ElementType.TYPE:说明该注解只能被声明在一个类前。
ElementType.FIELD:说明该注解只能被声明在一个类的字段前。
ElementType.METHOD:说明该注解只能被声明在一个类的方法前。
ElementType.PARAMETER:说明该注解只能被声明在一个方法参数前。
ElementType.CONSTRUCTOR:说明该注解只能声明在一个类的构造方法前。
ElementType.LOCAL_VARIABLE:说明该注解只能声明在一个局部变量前。
ElementType.ANNOTATION_TYPE:说明该注解只能声明在一个注解类型前。
ElementType.PACKAGE:说明该注解只能声明在一个包名前。
@Constraint来限定自定义注解的方法
@Documented
@Constraint(validatedBy = OrderNumberValidator.class)
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface OrderNumber {
String message() default "{com.acme.constraint.OrderNumber.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
- 自定义校验注解必须包含 message,groups,payload 属性。
String message() default [...];
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
import javax.validation.Constraint; import javax.validation.Payload;
import cn.netkiller.web.annotation.impl.MobileValidator;
@Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = MobileValidator.class) @Documented // 注解的实现类。 public @interface Mobile { // 校验错误的默认信息 String message() default “手机号码格式不正确!”;
// 是否强制校验
boolean isRequired() default true;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
<a name="oLSXd"></a>
### **校验器**
- **校验器**:对校验注解进行解析,实现 ConstraintValidator接口,接口使用了泛型,需要指定两个参数,第一个是**自定义注解类**,第二个是需要校验的**数据类型**,重写 isValid(T value, ConstraintValidatorContext context)方法。
```java
public interface ConstraintValidator<A extends Annotation, T> {
/**
* 在调用 isValid 方法之前初始化
* 该方法的参数类型为自定义注解,可调用注解的方法获取注解信息
* 重写该方法, 给自定义校验器中定义的类变量赋值
*/
default void initialize(A constraintAnnotation) {
}
/**
* 重写该方法,实现校验逻辑,返回 布尔值
*/
boolean isValid(T value, ConstraintValidatorContext context);
}
eg:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.util.StringUtils;
import cn.netkiller.web.annotation.Mobile;
public class MobileValidator implements ConstraintValidator<Mobile, String> {
public MobileValidator() {
// TODO Auto-generated constructor stub
}
private boolean required = false;
@Override
public void initialize(Mobile constraintAnnotation) {
required = constraintAnnotation.isRequired();
}
@Override
public boolean isValid(String phone, ConstraintValidatorContext constraintValidatorContext) {
Pattern mobile_pattern = Pattern.compile("1\\d{10}");
// System.out.println(phone);
// 是否为手机号的实现
if (required) {
if (StringUtils.isEmpty(phone)) {
return false;
}
Matcher m = mobile_pattern.matcher(phone);
return m.matches();
} else {
return StringUtils.isEmpty(phone);
}
}
}
使用
public class User {
// 这里是新添加的注解
@Mobile(message = "手机号码格式错误")
private String phone;
}
参考
https://beanvalidation.org/
http://www.gxitsky.com/article/1605537736100208
https://lfvepclr.gitbooks.io/spring-framework-5-doc-cn/content/5/5-8.html
http://lazycece.com/2019/02/16/springboot%E4%B8%AD%E5%8F%82%E6%95%B0%E6%A0%A1%E9%AA%8C%EF%BC%88validation%EF%BC%89%E6%B3%A8%E8%A7%A3%E8%87%AA%E5%AE%9A%E4%B9%89/