平时在使用SpringMVC的时候通常会对前端请求的参数进行验证,在不知道Validation前通常会在代码中直接验证参数合法性,这样做虽然也可以完成验证但,对业务代码的侵入性太强不利于代码维护,其实Spring 基于JSR 303(不了解JSR可以百度一下)规范提供能验证提供了参数验证功能,在这里记录一下Spring6中使用Validation验证参数。
1.新建Maven项目引入依赖(Spring略)
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>8.0.0.Final</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>jakarta.el</artifactId>
<version>5.0.0-M1</version>
</dependency>
1.1分析一下依赖关系,有助于理解Validation实现方式
在jakata中规定了一个JSR 303的java规范,用于规范化Validation实现,jakata本身并没有去实现这个规范,Hibernate实现这个规范为我们提供了ValidatorImpl,从图中可以看出jakata通过Java SPI机制(spring boot的自动配置也使用了类似的机制,SPI是Java自身提供的一种更原始的插件式服务提供方式)加载了Hibernate中Validator的实现,并且提供能ValidationFactory,在Spring 中也定义了一个Validator接口,为了适配JSR 303的Validator使用,Spring它提供了ValidatorAdapter,基于ValidatorAdapter我们可以轻松使用第三方Validator,当然我们也可以直接实现Spring的Validator,向Spring容器中注入Bean进行使用。
2.使用Spring Validation完成验证(简单示例)
2.1 自定义验证(此处只做演示,代码可以抽象为一个自定义validator)
@Configuration
public class ValidationConfig {
// @Bean
// public MethodValidationPostProcessor methodValidationPostProcessor() {
// return new MethodValidationPostProcessor();
// }
@Bean
public CustomValidatorBean customValidatorBean() {
CustomValidatorBean customValidatorBean = new CustomValidatorBean();
customValidatorBean.setValidatorFactory(Validation.buildDefaultValidatorFactory());
return customValidatorBean;
}
}
@Data
public class User{
@NotBlank
private String name;
@Max(100)
private Integer age;
}
@Service
public class ValidationTest {
@Autowired
CustomValidatorBean customValidatorBean;
public void userValidation(@Valid User user) throws BindException {
BindException bindException = new BindException(user, "user");
// 使用 ValidationUtils 调用验证
ValidationUtils.invokeValidator(customValidatorBean, user, bindException);
System.out.println(bindException.getErrorCount());
if(bindException.getErrorCount()>0){
throw bindException;
}
}
}
public class Main {
public static void main(String[] args) throws BindException {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext("com.cobin");
ValidationTest bean = context.getBean(ValidationTest.class);
User user = new User(); // name,age都为空会出现异常
bean.userValidation(user);
}
}
执行结果:
2.2 @Validated注解验证
@Configuration
public class ValidationConfig {
@Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
// 方法验证增强器
return new MethodValidationPostProcessor();
}
// @Bean
// public CustomValidatorBean customValidatorBean() {
// CustomValidatorBean customValidatorBean = new CustomValidatorBean();
// customValidatorBean.setValidatorFactory(Validation.buildDefaultValidatorFactory());
// return customValidatorBean;
// }
}
在需要提供验证的类上添加@Validated注解(也可以自定义注解,在Config中设置自定义的注解),需要验证的方法参数上添加@Valid,在Bean注入到容器中时,Spring会生成代理类实现验证逻辑。
@Service
@Validated
public class ValidationTest {
@Autowired
CustomValidatorBean customValidatorBean;
public void userValidation(@Valid User user) throws BindException {
BindException bindException = new BindException(user, "user");
// 使用 ValidationUtils 调用验证
ValidationUtils.invokeValidator(customValidatorBean, user, bindException);
System.out.println(bindException.getErrorCount());
if(bindException.getErrorCount()>0){
throw bindException;
}
}
}
主函数同2.1
执行结果:
在SpringMVC 控制层中为我们提供了更简单的使用方式,理解Spring 的Validation后我们可以不限于SpringMVC中使用,可以在Spring的环境中可以更灵活的使用Validation的校验功能,例如本人之前遇到过的RPC调用过程中的参数校验,由于但是没有充分了解这个细节,导致出现过一些问题,在Spring环境中使用Validation可以为我们减轻很多参数验证上的麻烦,不过上述也只是Validation使用方式的最简单部分。