@Validated
@Valid
Validator
ConstraintValidator
MethodValidationPostProcessor
@Data
public static class User {
@Length(min = 4, message = "名称最小{min}个字符")
private String userName;
// 嵌套验证
@Valid
@NotNull
private UserInfo userInfo;
}
@Data
public static class UserInfo {
@NotBlank(message = "性别不为空")
private String gender;
@NotNull(message = "年龄不为空")
private String age;
}
@Validated
@RestController
@RequestMapping("/validated")
public class ValidatedController {
@RequestMapping(method = { RequestMethod.POST })
public String validated(@Validated User user) {
System.out.println(user);
System.out.println("enter validated");
return "success";
}
@RequestMapping(value = "requestBody", method = { RequestMethod.POST })
public String validatedRequestBody(@RequestBody @Validated User user) {
System.out.println(user);
System.out.println("enter validatedRequestBody");
return "success";
}
}
@Valid
@RestController
@RequestMapping("/valid")
public class ValidController {
@RequestMapping(method = { RequestMethod.POST })
public String valid(@Valid User user) {
System.out.println(user);
System.out.println("enter valid");
return "success";
}
@RequestMapping(value = "requestBody", method = { RequestMethod.POST })
public String validRequestBody(@RequestBody @Valid User user) {
System.out.println(user);
System.out.println("enter validRequestBody");
return "success";
}
}
@Valid可以实现嵌套校验
@Validated可以进行分组校验
Validator
实现 org.springframework.validation.Validator 接口方式
@RequestMapping("/myInitBinder")
@Controller
public class InitBinderController {
@Autowired
private UserValidator userValidator;
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.addValidators(userValidator);
}
@RequestMapping(value = { "/validator" }, method = { RequestMethod.POST })
@ResponseBody
public String validator(@Validated User user) {
System.out.println(user);
return "success";
}
}
@Component
public static class UserValidator implements Validator {
@Override
public boolean supports(Class<?> clazz) {
// 只支持User类型对象的校验
return User.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
User user = (User) target;
String userName = user.getUserName();
if (StringUtils.isEmpty(userName) || userName.length() < 8) {
errors.rejectValue("userName", "valid.userNameLen",
new Object[] { "minLength", 8 }, "用户名不能少于{1}位");
}
}
}
ConstraintValidator
实现 javax.validation.ConstraintValidator 接口方式
@Same(field = "password", comparingField = "confirmPassword")
@Data
public static class TestBean {
@Size(min = 8, max = 128)
private String password;
private String confirmPassword;
}
@Documented
@Constraint(validatedBy = {SameValidator.class})
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(SameGroup.class)
@interface Same {
String message() default "{org.springframework.validation.beanvalidation.Same.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String field();
String comparingField();
@Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface List {
Same[] value();
}
}
public static class SameValidator implements ConstraintValidator<Same, Object> {
private String field;
private String comparingField;
private String message;
public void initialize(Same constraintAnnotation) {
field = constraintAnnotation.field();
comparingField = constraintAnnotation.comparingField();
message = constraintAnnotation.message();
}
public boolean isValid(Object value, ConstraintValidatorContext context) {
BeanWrapper beanWrapper = new BeanWrapperImpl(value);
Object fieldValue = beanWrapper.getPropertyValue(field);
Object comparingFieldValue = beanWrapper.getPropertyValue(comparingField);
boolean matched = ObjectUtils.nullSafeEquals(fieldValue, comparingFieldValue);
if (matched) {
return true;
}
else {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message)
.addPropertyNode(field)
.addConstraintViolation();
return false;
}
}
}
@RequestMapping(value = "constraintValidator", method = {RequestMethod.POST})
public String constraintValidator(@Validated TestBean testBean) {
System.out.println(testBean);
return "success";
}
@RequestMapping(value = "constraintValidator2", method = {RequestMethod.POST})
public String constraintValidator2(TestBean testBean) {
BeanPropertyBindingResult errors = new BeanPropertyBindingResult(testBean, "testBean");
validatorAdapter.validate(testBean, errors);
System.out.println(errors);
return "success";
}
以上几种都可以使用 @Validated和@Valid 到 @RequestMapping 参数上,调用逻辑如下:
// org.springframework.validation.DataBinder
public void validate(Object... validationHints) {
Object target = getTarget();
Assert.state(target != null, "No target to validate");
BindingResult bindingResult = getBindingResult();
// Call each validator with the same binding result
for (Validator validator : getValidators()) {
if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
((SmartValidator) validator).validate(target, bindingResult, validationHints);
}
else if (validator != null) {
validator.validate(target, bindingResult);
}
}
}
通过实现 ConstraintValidator 的方式可以通过 API 方式调用 :
// SmartValidator
void validate(Object target, Errors errors)
void validate(Object target, Errors errors, Object... validationHints);
MethodValidationPostProcessor
public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor
implements InitializingBean {
private Class<? extends Annotation> validatedAnnotationType = Validated.class;
@Override
public void afterPropertiesSet() {
Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));
}
protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
}
}
被 @Validated 注解的类会被代理,通过 MethodValidationInterceptor 处理方法级的验证
@RequestMapping(value = "method", method = {RequestMethod.GET})
public String method() {
helloService.hello(1, null);
System.out.println("enter validatedRequestBody");
return "success";
}
@Validated(Default.class)
public interface HelloService {
@NotEmpty String hello(@NotNull @Min(10) Integer id, @NotNull String name);
void validatedUser(@Valid User user);
}
@Slf4j
@Service
public static class HelloServiceImpl implements HelloService {
@Override
public String hello(Integer id, String name) {
User user = new User();
HelloService helloService = (HelloService) AopContext.currentProxy();
helloService.validatedUser(user);
return null;
}
@Override
public void validatedUser(User user) {
System.out.println(user);
}
}
相关类定义
// ValidationAutoConfiguration
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean(Validator.class)
public static LocalValidatorFactoryBean defaultValidator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
@Bean
@ConditionalOnMissingBean
public static MethodValidationPostProcessor methodValidationPostProcessor(Environment environment,
@Lazy Validator validator, ObjectProvider<MethodValidationExcludeFilter> excludeFilters) {
FilteredMethodValidationPostProcessor processor = new FilteredMethodValidationPostProcessor(
excludeFilters.orderedStream());
boolean proxyTargetClass = environment.getProperty("spring.aop.proxy-target-class", Boolean.class, true);
processor.setProxyTargetClass(proxyTargetClass);
processor.setValidator(validator);
return processor;
}
// WebMvcAutoConfiguration#EnableWebMvcConfiguration
@Bean
@Override
public Validator mvcValidator() {
if (!ClassUtils.isPresent("javax.validation.Validator", getClass().getClassLoader())) {
return super.mvcValidator();
}
return ValidatorAdapter.get(getApplicationContext(), getValidator());
}
// ValidatorAdapter
public static Validator get(ApplicationContext applicationContext, Validator validator) {
if (validator != null) {
return wrap(validator, false);
}
return getExistingOrCreate(applicationContext);
}
private static Validator getExistingOrCreate(ApplicationContext applicationContext) {
Validator existing = getExisting(applicationContext);
if (existing != null) {
return wrap(existing, true);
}
return create();
}