Spring 校验使用场景
• Spring 常规校验(Validator)
• Spring 数据绑定(DataBinder)
• Spring Web 参数绑定(WebDataBinder)
• Spring Web MVC / Spring WebFlux 处理方法参数校验
Validator 接口设计
• 接口职责
- Spring 内部校验器接口,通过编程的方式校验目标对象
• 核心方法
- supports(Class):校验目标类能否校验
- validate(Object,Errors):校验目标对象,并将校验失败的内容输出至 Errors 对象
• 配套组件
- 错误收集器:org.springframework.validation.Errors
- Validator 工具类:org.springframework.validation.ValidationUtils
Errors 接口设计
• 接口职责
- 数据绑定和校验错误收集接口,与 Java Bean 和其属性有强关联性
• 核心方法
- reject 方法(重载):收集错误文案
- rejectValue 方法(重载):收集对象字段中的错误文案
• 配套组件
- Java Bean 错误描述:org.springframework.validation.ObjectError
- Java Bean 属性错误描述:org.springframework.validation.FieldError
Errors 文案来源
• Errors 文案生成步骤
- 选择 Errors 实现(如:org.springframework.validation.BeanPropertyBindingResult)
- 调用 reject 或 rejectValue 方法
- 获取 Errors 对象中 ObjectError 或 FieldError
- 将 ObjectError 或 FieldError 中的 code 和 args,关联 MessageSource 实现(如:
ResourceBundleMessageSource)
/**
* 错误文案示例
*/
public class ErrorsMessageDemo {
public static void main(String[] args) {
// 0. 创建 User 对象
User user = new User();
user.setName("小马哥");
// 1. 选择 Errors - BeanPropertyBindingResult
Errors errors = new BeanPropertyBindingResult(user, "user");
// 2. 调用 reject 或 rejectValue
// reject 生成 ObjectError
// reject 生成 FieldError
errors.reject("user.properties.not.null");
// user.name = user.getName()
errors.rejectValue("name", "name.required");
// 3. 获取 Errors 中 ObjectError 和 FieldError
// FieldError is ObjectError
List<ObjectError> globalErrors = errors.getGlobalErrors();
List<FieldError> fieldErrors = errors.getFieldErrors();
List<ObjectError> allErrors = errors.getAllErrors();
// 4. 通过 ObjectError 和 FieldError 中的 code 和 args 来关联 MessageSource 实现
MessageSource messageSource = createMessageSource();
for (ObjectError error : allErrors) {
String message = messageSource.getMessage(error.getCode(), error.getArguments(), Locale.getDefault());
System.out.println(message);
}
}
static MessageSource createMessageSource() {
StaticMessageSource messageSource = new StaticMessageSource();
messageSource.addMessage("user.properties.not.null", Locale.getDefault(), "User 所有属性不能为空");
messageSource.addMessage("id.required", Locale.getDefault(), "the id of User must not be null.");
messageSource.addMessage("name.required", Locale.getDefault(), "the name of User must not be null.");
return messageSource;
}
}
自定义 Validator
• 实现 org.springframework.validation.Validator 接口
- 实现 supports 方法
- 实现 validate 方法
- 通过 Errors 对象收集错误
- ObjectError:对象(Bean)错误:
- FieldError:对象(Bean)属性(Property)错误
- 通过 ObjectError 和 FieldError 关联 MessageSource 实现获取最终文案
- 通过 Errors 对象收集错误
/**
* 自定义 Spring {@link Validator} 示例
*/
public class ValidatorDemo {
public static void main(String[] args) {
// 1. 创建 Validator
Validator validator = new UserValidator();
// 2. 判断是否支持目标对象的类型
User user = new User();
System.out.println("user 对象是否被 UserValidator 支持检验:" + validator.supports(user.getClass()));
// 3. 创建 Errors 对象
Errors errors = new BeanPropertyBindingResult(user, "user");
validator.validate(user, errors);
// 4. 获取 MessageSource 对象
MessageSource messageSource = createMessageSource();
// 5. 输出所有的错误文案
for (ObjectError error : errors.getAllErrors()) {
String message = messageSource.getMessage(error.getCode(), error.getArguments(), Locale.getDefault());
System.out.println(message);
}
}
static class UserValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
return User.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "id", "id.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "name", "name.required");
String userName = user.getName();
// ...
}
}
}
Validator 的救赎
• Bean Validation 与 Validator 适配
- 核心组件 - org.springframework.validation.beanvalidation.LocalValidatorFactoryBean
- 依赖 Bean Validation - JSR-303 or JSR-349 provider
- Bean 方法参数校验 - org.springframework.validation.beanvalidation.MethodValidationPostProcessor
<?xml version="1.0" encoding="UTF-8"?>
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<context:component-scan base-package="org.geekbang.thinking.in.spring.validation"/>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
</bean>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="validator" ref="validator"/>
</bean>
</beans>
/**
* Spring Bean Validation 整合示例
*/
public class SpringBeanValidationDemo {
public static void main(String[] args) {
// 配置 XML 配置文件
// 启动 Spring 应用上下文
ConfigurableApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/META-INF/bean-validation-context.xml");
// Validator validator = applicationContext.getBean(Validator.class);
// System.out.println(validator instanceof LocalValidatorFactoryBean);
UserProcessor userProcessor = applicationContext.getBean(UserProcessor.class);
userProcessor.process(new User());
// 关闭应用上下文
applicationContext.close();
}
@Component
@Validated
static class UserProcessor {
public void process(@Valid User user) {
System.out.println(user);
}
}
static class User {
@NotNull
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
}
面试题
Spring 校验接口是哪个?
答:org.springframework.validation.Validator
Spring 有哪些校验核心组件?
答:
• 检验器:org.springframework.validation.Validator
• 错误收集器:org.springframework.validation.Errors
• Java Bean 错误描述:org.springframework.validation.ObjectError
• Java Bean 属性错误描述:org.springframework.validation.FieldError
• Bean Validation 适配:org.springframework.validation.beanvalidation.LocalValidatorFactoryBean