Spring-Validation(数据校验)
Spring-Validation(数据校验)
Spring框架中的数据校验模块Validation主要用于验证用户输入或业务数据的合法性。它支持自定义验证逻辑,并与Spring MVC无缝集成。Spring的校验模块不仅支持原生的Validator接口,还兼容JSR-303/JSR-380(Bean Validation)标准,提供了强大的数据校验能力。
数据校验(Validation)模块中的核心组件
1.Validator接口
Validator是Spring提供的原生校验接口,用于实现自定义校验逻辑。它包含了两个核心方法。
boolean supports(Class<?> clazz);
判断当前校验器是否支持指定类型的对象
返回值:
true表示支持,false表示不支持
void validate(Object target, Errors errors);
执行校验逻辑,并将错误信息记录到Errors对象中
参数:
target:需要校验的对象
errors:用于存储校验错误信息
2.LocalValidatorFactoryBean工厂类
LocalValidatorFactoryBean是Spring框架中的一个类,用于在Spring应用中配置和提供Bean Vaildation的Validator实例。它实现了Spring的Validator接口,并将底层的Bean Validation提供者(如Hibernate Validator)集成到Spring应用中。
3.Errors接口
Errors接口用于存储校验过程中的错误信息,这里只介绍常用的方法。
void reject(String errorCode);
添加全局错误
参数:
errorCode:错误码
void rejectValue(@Nullable String field, String errorCode, String defaultMessage);
添加字段级别错误
参数:
field:字段名
errorCode:错误码
defaultMessage:错误回退的默认消息
boolean hasErrors();
判断是否存在错误
FieldError getFieldError(String field);
获取指定字段名关联的第一个错误
参数:
field:字段名
4.ValidationUtils工具类
ValidationUtils是Spring提供的一个工具类,封装了常见的校验操作,以下是常用方法。
public static void rejectIfEmpty(Errors errors, String field, String errorCode, String defaultMessage)
如果字段为空,则记录错误
参数:
errors:要在该Errors实例上添加错误信息
field:字段名
errorCode:错误码
defaultMessage:错误回退的默认消息
public static void rejectIfEmptyOrWhitespace(Errors errors, String field, String errorCode, String defaultMessage)
如果字段为空或仅包含空白字符,则记录错误
参数:
errors:要在该Errors实例上添加错误信息
field:字段名
errorCode:错误码
defaultMessage:错误回退的默认消息
5.BindingResult接口
BindingResult是Errors接口的子接口,用于在Spring MVC中存储校验结果
主要提供了获取字段错误信息、判断是否存在错误、添加自定义错误信息等功能。
6.JSR-303/JSR-380(Bean Validation)支持
Spring集成了JSR-303/JSR-380(Bean Validation)标准,支持通过注解方式定义校验规则。常用的注解如下
JSR-303/JSR-380(Bean Validation)常用注解
| 注解 | 说明 |
|---|---|
| @NotNull | 限制必须不为null |
| @Null | 限制必须为null |
| @NotEmpty | 作用于集合、数组、字符串等不能为空并且长度不为0 |
| @NotBlank | 作用于字符串不能为空并且不能只包含空白字符 |
| @Size(min=xx,max=xx) | 限制集合、数组、字符串等长度必须在min-max的数值之间 |
| @Max(xx) | 作用于数值类型,必须不大于指定的数值 |
| @Min(xx) | 作用于数值类型,必须不小于指定的数值 |
| @DecimalMax | 作用于数值类型(包括小数),必须不大于指定的数值 |
| @DecimalMin | 作用于数值类型(包括小数),必须不小于指定的数值 |
| @Past | 作用于日期或时间,必须在当前时间之前 |
| @Future | 作用于日期或时间,必须在当前时间之后 |
| @Pattern | 作用于字符串,必须符合指定的正则表达式 |
| 作用于字符串,必须是有效的电子邮箱地址 | |
| @AssertTrue | 作用于布尔类型,必须为true |
| @AssertFalse | 作用于布尔类型,必须为false |
| @Valid | 用于标注对象,表示对象的属性也需要验证(级联验证) |
Spring Validation(数据校验)特有的注解@Validated与JSR-303/JSR-380(Bean Validation)标准的注解@Valid的区别
相同点
- 目的:两者都用于在Spring框架中进行数据验证,确保对象的状态符合特定的规则和约束。
- 触发验证:当标注在方法参数或对象属性上时,两者都会触发验证过程。
- 级联验证:都支持级联验证,即可以验证嵌套对象。
- 异常抛出:验证失败时,都会抛出响应的异常,如ConstraintViolationException或MethodArgumentNotValidException。
不同点
- 来源
@Valid:来源于JSR-303/JSR-380 Bean Validation规范,是Java标准的一部分。
@Validated:是Spring特有的注解,用于提供Spring的验证支持。
- 使用范围
@Valid:可以用于方法、构造函数、方法参数、成员属性上。
@Validated:通常用于类,用来启用该类的验证支持,也可以用于方法参数。
- 分组验证
@Valid:不支持分组验证。
@Validated:支持分组验证,可以指定一个或多个分组(使用groups属性)来执行部分验证。
- 与Spring的集成
@Valid:在Spring中,当用于方法参数时,会触发验证,但不会提供Spring特有的验证功能,如分组验证。
@Validated:与Spring的验证框架紧密集成,支持分组验证和Srping的验证器(Validator)。
- 异常处理
@Valid:验证失败时,会抛出ConstraintViolationException。
@Validated:验证失败时,可以抛出ConstraintViolationException或Spring特定的MethodArgumentNotValidException。
- 注解属性
@Valid:没有额外的属性。
@Validated:有value和groups属性,可以指定验证器或分组。
7.ConstraintValidator接口
ConstraintValidator是Java Bean Validation API(JSR-303/JSR-380)中的一个核心接口,用于实现自定义校验逻辑。通过实现这个接口,开发者可以创建自定义的校验注解,并将其应用到Java Bean的字段或方法上。
ConstraintValidator接口源码
public interface ConstraintValidator<A extends Annotation, T> {
default void initialize(A constraintAnnotation) {
}
boolean isValid(T value, ConstraintValidatorContext context);
}
ConstraintValidator接口方法说明
default void initialize(A constraintAnnotation)
初始化校验器,在校验器实例化后,该方法会被调用一次
参数:
constrainAnnotation:自定义注解的实例,可以通过它获取注解中定义的参数
boolean isValid(T value, ConstraintValidatorContext context)
执行实际的校验逻辑
参数:
value:需要校验的值
context:校验上下文,用于自定义错误信息或控制校验行为
返回值:
true:校验通过
false:校验失败
数据校验(Validation)模块的四种实现方式
四种实现方式
- 通过实现Validator接口进行数据校验
- 通过注解形式进行数据校验
- 基于方法进行数据校验
- 基于自定义校验注解进行数据校验
引入相关依赖
<!--spring context依赖-->
<!--当你引入Spring Context依赖之后,表示将Spring的基础依赖引入了-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.11</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>7.0.5.Final</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>4.0.1</version>
</dependency>
通过实现Validator接口进行数据校验的示例
实体类
/**
* 人信息类
*/
public class Person {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Validator接口实现类
/**
* 通过实现spring中的Validator类来实现数据校验
*/
public class PersonValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return Person.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
//name不能为空
ValidationUtils.rejectIfEmpty(errors, "name", "name.empty", "name is empty");
//0<age<200
Person person = (Person) target;
if (0 >= person.getAge()) {
errors.rejectValue("age", "age.error", "age <= 0");
} else if (200 <= person.getAge()) {
errors.rejectValue("age", "age.error", "age >= 200");
}
}
}
测试类
/**
* 测试数据校验
*/
public class TestPerson {
public static void main(String[] args) {
//创建person对象
Person person = new Person();
person.setName("alex");
person.setAge(20);
//创建person对应databinder
DataBinder dataBinder = new DataBinder(person);
//设置校验器
dataBinder.setValidator(new PersonValidator());
//调用方法执行校验器
dataBinder.validate();
//输出校验结果
BindingResult bindingResult = dataBinder.getBindingResult();
System.out.println(bindingResult.getAllErrors());
}
}
通过注解形式进行数据校验的示例
使用JSR-303/JSR-380(Bean Validation)标准的注解进行校验的实体类
/**
* 用户信息类
*
* @NotNull 限制必须不为null
* @NotEmpty 只作用于字符串类型,字符串不为空,并且长度不为0
* @NotBlank 只作用于字符串类型,字符串不为空,并且trim()后不为空串
* @DecimalMax(value) 限制必须为一个不大于指定值的数字
* @DecimalMin(value) 限制必须为一个不小于指定值的数字
* @Max(value) 限制必须为一个不大于指定值的数字
* @Min(value) 限制必须为一个不小于指定值的数字
* @Pattern(value) 限制必须符合指定的正则表达式
* @Size(max,min) 限制字符长度必须在min到max之间
* @Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
*/
public class User {
@NotNull
private String name;
@Max(200)
@Min(1)
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
java配置类(将LocalValidatorFactoryBean工厂类注册到容器中,用于对Validator进行依赖注入)
/**
* 配置类
*/
@Configuration
@ComponentScan("com.shen.validator.modetwo")
public class ValidatorConfig {
@Bean
public LocalValidatorFactoryBean vaildator() {
return new LocalValidatorFactoryBean();
}
}
注入原生的校验器的校验类(jakarta.validation.Validator)
/**
* 注入原生的校验器
*/
@Component
public class MyValidatorOne {
@Autowired
private Validator validator;
/**
* 返回是否有校验不通过的地方
* @param user 用户实例
* @return 是否有校验不通过的地方
*/
public boolean validatorUser(User user) {
Set<ConstraintViolation<User>> validate = validator.validate(user);
for (ConstraintViolation<User> info : validate) {
System.out.println(info.toString());
}
return validate.isEmpty();
}
}
注入Spring提供的校验器的校验类(org.springframework.validation.Validator)
/**
* 注入Spring依赖的校验器
*/
@Component
public class MyValidatorTwo {
@Autowired
private Validator validator;
/**
* 返回是否有校验不通过的地方
*
* @param user 用户实例
* @return 是否有校验不通过的地方
*/
public boolean validatorUser(User user) {
BindException bindException = new BindException(user, user.getName());
validator.validate(user, bindException);
System.out.println(bindException.getBindingResult().getAllErrors());
return !bindException.hasErrors();
}
}
测试两种校验器的测试类
/**
* 测试两种校验器
*/
public class TestMyValidator {
@Test
public void TestValidatorOne() {
ApplicationContext context = new AnnotationConfigApplicationContext(ValidatorConfig.class);
MyValidatorOne myValidatorOne = context.getBean(MyValidatorOne.class);
User user = new User();
user.setName("alex");
user.setAge(20);
boolean message = myValidatorOne.validatorUser(user);
System.out.println(message);
}
@Test
public void TestValidatorTwo() {
ApplicationContext context = new AnnotationConfigApplicationContext(ValidatorConfig.class);
MyValidatorTwo myValidatorTwo= context.getBean(MyValidatorTwo.class);
User user = new User();
// user.setName("alex");
// user.setAge(20);
boolean message = myValidatorTwo.validatorUser(user);
System.out.println(message);
}
}
基于方法进行数据校验的示例
使用JSR-303/JSR-380(Bean Validation)标准的注解进行校验的实体类
/**
* 用户信息类
*
* @NotNull 限制必须不为null
* @NotEmpty 只作用于字符串类型,字符串不为空,并且长度不为0
* @NotBlank 只作用于字符串类型,字符串不为空,并且trim()后不为空串
* @DecimalMax(value) 限制必须为一个不大于指定值的数字
* @DecimalMin(value) 限制必须为一个不小于指定值的数字
* @Max(value) 限制必须为一个不大于指定值的数字
* @Min(value) 限制必须为一个不小于指定值的数字
* @Pattern(value) 限制必须符合指定的正则表达式
* @Size(max,min) 限制字符长度必须在min到max之间
* @Email 验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式
*/
public class User {
@NotNull
private String name;
@Max(200)
@Min(1)
private int age;
@Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$", message = "手机号码格式错误")
@NotBlank(message = "手机号码不能为空")
private String phone;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
}
java配置类(配置MethodValidationPostProcessor)
/**
* 配置类
*/
@Configuration
@ComponentScan("com.shen.validator.modethree")
public class ValidatorConfig {
@Bean
public MethodValidationPostProcessor vaildator() {
return new MethodValidationPostProcessor();
}
}
基于方法实现的校验类
/**
* 基于方法实现校验
*/
@Component
@Validated
public class MyValidator {
public String testMethod(@NotNull @Valid User user) {
return user.toString();
}
}
测试校验方法的测试类
public class TestMyValidator {
@Test
public void testValidator() {
ApplicationContext context = new AnnotationConfigApplicationContext(ValidatorConfig.class);
MyValidator myValidator = context.getBean(MyValidator.class);
User user = new User();
user.setName("alex");
user.setAge(550);
user.setPhone("13999999999");
System.out.println(myValidator.testMethod(user));
}
}
基于自定义校验注解进行数据校验的示例
自定义注解
/**
* 自定义注解,实现自定义数据校验
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = {CanNotBlankValidator.class}
)
public @interface CanNotBlank {
//默认提示的错误信息
String message() default "该元素不能为空";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface List {
CanNotBlank[] value();
}
}
自定义校验器,关联自定义注解,重写校验方法
/**
* 自定义校验器
*/
public class CanNotBlankValidator implements ConstraintValidator<CanNotBlank, String> {
@Override
public boolean isValid(String s, ConstraintValidatorContext context) {
//字段不能包含空格字符
if (null != s && s.contains(" ")) {
//获取默认提示信息
String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
System.out.println("default message :" + defaultConstraintMessageTemplate);
//禁用默认提示信息
context.disableDefaultConstraintViolation();
//设置提示语
context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
return false;
}
return true;
}
}
引入自定义校验注解进行校验的实体类
public class User {
@NotNull
private
String name;
@Max(200)
@Min(1)
private int age;
@CanNotBlank
private String message;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
java配置类(配置MethodValidationPostProcessor)
/**
* 配置类
*/
@Configuration
@ComponentScan("com.shen.validator.modethree")
public class ValidatorConfig {
@Bean
public MethodValidationPostProcessor vaildator() {
return new MethodValidationPostProcessor();
}
}
基于方法实现的校验类
/**
* 基于方法实现校验
*/
@Component
@Validated
public class MyValidator {
public String testMethod(@NotNull @Valid User user) {
return user.toString();
}
}
测试自定义校验注解的测试类
public class TestUser {
@Test
public void testValidator() {
ApplicationContext context = new AnnotationConfigApplicationContext(ValidatorConfig.class);
MyValidator myValidator = context.getBean(MyValidator.class);
User user = new User();
user.setName("alex");
user.setAge(20);
user.setMessage("hello world");
System.out.println(myValidator.testMethod(user));
}
}
参考资料
https://docs.spring.io/spring-framework/reference/core/validation.html

浙公网安备 33010602011771号