spring之Validator

初步认识

spring数据验证核心类:①:Validator ②:Errors,两者之间的纽带是Validator中定义的validate方法。

public interface Validator {
	// 限定Validator的职责,不可能所有的校验全部交给一个Validator来做
	boolean supports(Class<?> clazz);
	
	// 将target校验错误信息放入Errors中
	void validate(Object target, Errors errors);
}
简单使用
@Data
public class Customer {
    private String name;

    private String sex;

    private PhoneNumber phoneNumber;
}
@Data
public class PhoneNumber {
    private String number;

    private String areaCode;
}
public class CustomerValidator implements Validator {
    private PhoneNumberValidator phoneNumberValidator;

    @Override
    public boolean supports(Class<?> aClass) {
        return ClassUtils.isAssignable (aClass,Customer.class);
    }

    @Override
    public void validate(Object target, Errors errors) {
        Customer customer = (Customer) target;
        // 最后一个参数可以替换掉messages_zh_CN.properties中对应errorCode消息中的占位符
        ValidationUtils.rejectIfEmpty (errors,"name","name.empty",new Object[]{1,2});
        ValidationUtils.rejectIfEmpty (errors,"sex","sex.empty");
        PhoneNumber phoneNumber = customer.getPhoneNumber ();
        // 这里涉及到嵌套校验,需要改变校验对象的上下文路径
        // 这个名称并不是随便写的,和你声明的对象名称一致就好了,如果是list,比如List<PhoneNumber> 
        // phoneNumbers ;那么这里就变成循环校验,上下文路径就应该是phoneNumbers[i],i是循环变量,1,2,3...
        errors.pushNestedPath ("phoneNumber");
        ValidationUtils.invokeValidator (phoneNumberValidator,phoneNumber,errors);
        errors.popNestedPath ();
    }

    public void setPhoneNumberValidator(PhoneNumberValidator phoneNumberValidator) {
        this.phoneNumberValidator = phoneNumberValidator;
    }
}
public class PhoneNumberValidator implements Validator {

    @Override
    public boolean supports(Class<?> aClass) {
        return ClassUtils.isAssignable (aClass,PhoneNumber.class);
    }

    @Override
    public void validate(Object target, Errors errors) {
        PhoneNumber phoneNumber = (PhoneNumber) target;
        if(phoneNumber == null)
            errors.reject ("PhoneNumber is null");
        if(!StringUtils.isNumeric (phoneNumber.getAreaCode ()))
            errors.rejectValue ("areaCode","areaCode.numeric","areaCode cannot be empty");
        if(!StringUtils.isNumeric (phoneNumber.getNumber ()))
            errors.rejectValue ("number","number.numeric");
    }
}
public class TestDemo {
    public static void main(String[] args) {
        CustomerValidator customerValidator = new CustomerValidator ();
        customerValidator.setPhoneNumberValidator (new PhoneNumberValidator ());
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource ();
        messageSource.setBasename ("messages");
        Customer customer = new Customer ();
        PhoneNumber phoneNumber = new PhoneNumber ();
        customer.setPhoneNumber (phoneNumber);
        BindException errors = new BindException (customer, "customer");
        ValidationUtils.invokeValidator (customerValidator,customer,errors);
        if(errors.hasErrors ()){
            List<ObjectError> allErrors = errors.getAllErrors ();
            for (int i = 0; i < allErrors.size (); i++) {
                String message = messageSource.getMessage (allErrors.get (i).getCode (), allErrors.get (i).getArguments (), Locale.CHINA);
                System.out.println (message);
            }
        }
    }
}

在这里插入图片描述
在这里插入图片描述
当然在spring中,我们没有必要自己去定义Validator,它给我们提供了相关的实现类LocalValidatorFactoryBean。
在这里插入图片描述
但是它并未帮助我们实现校验的相关逻辑,想想也不可能嘛,它咋知道我们想要怎样的校验,于是它委托其他类来实现,同时我们也不需要自己绑定消息源了,我们只要告诉消息源的位置就可以了。
这里委托校验的类很关键,而已知的hibernate校验框架就可以实现这个功能,它内置了很多的约束校验器,我们只需通过注解就能完成对应的校验功能,当然也可以自定义,这个放在后面讲。
springMVC中我们可以这么配置:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
	<property name="providerClass" value="org.hibernate.validator.HibernateValidator"/>
	<property name=''validationMessageSource'' value=""/>
</bean>

其实我们在pom.xml中引入了hibernate-validator的jar包后,provideClass这个属性我们可以不用配置,LocalValidatorFactoryBean在初始化的时候,如果发现provideClass未配置,会去jar包中找。所以如果使用springBoot,我们完全可以不用进行配置,当然如果需要指定消息源,则需要进行相关配置了。

@Configuration
public class CustomConfiguration {
    @Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasenames("classpath:/messages");
        messageSource.setUseCodeAsDefaultMessage(false);
        messageSource.setCacheSeconds((int) TimeUnit.HOURS.toSeconds(1));
        messageSource.setFallbackToSystemLocale(false);
        return messageSource;
    }
    @Bean
    public LocalValidatorFactoryBean validator() {
        LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
        factoryBean.setValidationMessageSource(messageSource());
        return factoryBean;
    }
}
自定义约束校验器
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Constraint(validatedBy = NotXListValidator.class)
public @interface NotXList {

    String message() default "Should not be X";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}
public class NotXListValidator implements ConstraintValidator<NotXList, List<String>> {
    @Override
    public void initialize(NotXList constraintAnnotation) {
    }

    @Override
    public boolean isValid(List<String> list, ConstraintValidatorContext context) {
        boolean valid = true;
        for (int i = 0; i < list.size(); i++) {
            if ("X".equals(list.get(i))) {
                valid = false;
            }
        }
        return valid;
    }
}
  public class ListContainer {
    @NotXList
    private List<String> list = new LinkedList<>();

    public void addString(String value) {
        list.add(value);
    }

    public List<String> getList() {
        return list;
    }
    public static void main(String[] args) {
    	LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
        validator.afterPropertiesSet();

        ListContainer listContainer = new ListContainer();
        listContainer.addString("A");
        listContainer.addString("X");

        BeanPropertyBindingResult errors = new BeanPropertyBindingResult(listContainer, "listContainer");
        validator.validate(listContainer, errors);
    }
}

一些简单的校验器Hibernate校验框架已经提供了,大家可以参考,我用的6.0.20的包,都位于org.hibernate.validator.internal.constraintvalidators.bv包下面。

请求参数的校验
// 使用@Valid或者@Validated将会对请求参数进行校验
@Controller
@RequestMapping("/test")
public class ValidationTestController {
    @ResponseBody
    @RequestMapping("/test1")
    public String test1(@Valid User user){
        return "test1";
    }
}

原理:请求参数在解析的过程中会进行校验,下面是校验的逻辑

protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
		Annotation[] annotations = parameter.getParameterAnnotations();
		for (Annotation ann : annotations) {
			Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
			if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
				Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
				Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});
				binder.validate(validationHints);
				break;
			}
		}
	}
方法校验
public interface MyValidInterface<T> {

	@NotNull Object myValidMethod(@NotNull String arg1, @Max(10) int arg2);

	T myGenericMethod(@NotNull T value);
}
// 这个注解一定要有,不然不起作用
@Validated
@Service
public  class MyValidBean implements MyValidInterface<String> {
	@Override
	public Object myValidMethod(String arg1, int arg2) {
		return (arg2 == 0 ? null : "value");
	}
	
	@Override
	public String myGenericMethod(String value) {
		return value;
	}
}

springBoot中

@Controller
public class ValidController{
	@Autowired
	private MyValidBean validBean;
	
	@RequestBody
	@RequestMapping("/valid")
	public String testValid(String type,int age){
		// 这里如果参数不满足指定方法的校验要求,就会报错
		return validBean.myValidMethod(type,age);
	}
}

spring中需要配置MethodValidationPostProcessor

<bean id="methodValidationPostProcessor" class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
</bean>

MethodValidationPostProcessor的实现原理就是定义了一个切面,这有关springAOP的内容,在这篇博客里面我不打算深入讲解。

有关Validator的内容暂时先写到这里,以后有内容需要补充再记录。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是Spring Validator的Maven依赖: ``` <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.3.9</version> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.2.0.Final</version> </dependency> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值