参数校验 是web项目中必须要做的事情。spring框架中有提供 spring bean validation,其底层就是封装的hibernate validation,而hibernate validation是 java bean validation规范的具体实现。
因此,先了解下hibernate validation的具体使用,再来探究下spring bean validation的使用和原理。
1 hibernate validation了解与使用
先创建个springboot项目,方便后面spring bean validation使用。
pom.xml
<?xml version="1.0" encoding="UTF-8"?> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">org.springframework.boot
spring-boot-starter-parent2.1.5.RELEASE4.0.0com.xtl
bean-validation1.0-SNAPSHOTorg.springframework.boot
spring-boot-starter-weborg.projectlombok
lombok1.16.18org.springframework.boot
spring-boot-maven-plugin
javax.validation,hibernate-validator不需要额外导入了,项目中已经包含了这两个依赖了
![86a95ff7e4841dbcb7d92a23554b078a.png](https://img-blog.csdnimg.cn/img_convert/86a95ff7e4841dbcb7d92a23554b078a.png)
![b47562ee80687cd7c1b40fb7031a431a.png](https://img-blog.csdnimg.cn/img_convert/b47562ee80687cd7c1b40fb7031a431a.png)
这两个接口,定义了校验方法
1.1 对java bean进行校验
Validator中的接口方法:
Set> validate(T var1, Class>... var2)
校验bean对象中的所有约束。
接下来,示例演示:
创建ValidateUser对象
@Datapublic class ValidateUser { @NotNull private String name; @NotNull @Min(0) private Integer age;}
写个测试方法
/** * java bean validate */public static void test1() { ValidateUser user = new ValidateUser(); user.setAge(-2); ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); Validator validator = factory.getValidator(); Set> result = validator.validate(user); result.stream().forEach(v -> { Path propertyPath = v.getPropertyPath(); String message = v.getMessage(); Object invalidValue = v.getInvalidValue(); System.out.println(propertyPath + " " + message + ":" + invalidValue); });}
Main方法中运行,查看结果
![9e63fc4522a9b14843cf5e4c21312256.png](https://img-blog.csdnimg.cn/img_convert/9e63fc4522a9b14843cf5e4c21312256.png)
1.1 对方法参数进行校验
方法参数校验,我们用ExecutableValidator接口中的validateParameters方法
Set> validateParameters(T var1, Method var2, Object[] var3, Class>... var4)
先创建一个工作类 获取校验器
![647282779d174c212b4b23a16574c58a.png](https://img-blog.csdnimg.cn/img_convert/647282779d174c212b4b23a16574c58a.png)
创建测试方法
![279866546411b120a0c6f1f85d89dffb.png](https://img-blog.csdnimg.cn/img_convert/279866546411b120a0c6f1f85d89dffb.png)
测试运行,结果
![0c519202164bd4f23c3f2f3494f60ea8.png](https://img-blog.csdnimg.cn/img_convert/0c519202164bd4f23c3f2f3494f60ea8.png)
有时方法参数是个对象,此时如果要对对象中的属性进行校验 需要再加@Valid注解
![0f8b7e1f76c8a387ea12a925dd24bd70.png](https://img-blog.csdnimg.cn/img_convert/0f8b7e1f76c8a387ea12a925dd24bd70.png)
进行测试
![e94a8db878aa043825d98e9c72f7be3d.png](https://img-blog.csdnimg.cn/img_convert/e94a8db878aa043825d98e9c72f7be3d.png)
不加@Valid注解,校验通过,
加@Valid注解后,参数对象的属性才会被校验
1.1 对方法返回值进行校验
返回值校验,用到ExecutableValidator接口中的validateReturnValue方法
Set> validateReturnValue(T var1, Method var2, Object var3, Class>... var4)
示例;
![2aa96e977a665a109c4dca6701d135e2.png](https://img-blog.csdnimg.cn/img_convert/2aa96e977a665a109c4dca6701d135e2.png)
测试运行结果
![00dd74ac9007f5488a6e9f3a7320e1f1.png](https://img-blog.csdnimg.cn/img_convert/00dd74ac9007f5488a6e9f3a7320e1f1.png)
2 spring项目bean validation
2.1 json传参对象校验
![841ca8c9b3301d1ab518104dbc7905d7.png](https://img-blog.csdnimg.cn/img_convert/841ca8c9b3301d1ab518104dbc7905d7.png)
![bc6ee07be1f7e95ddff01cb702838009.png](https://img-blog.csdnimg.cn/img_convert/bc6ee07be1f7e95ddff01cb702838009.png)
在对象上加上@Validated或@Valid注解都可以
在访问该接口时,spring会先校验参数对象
校验不通过的话,会抛出MethodArgumentNotValidException异常,spring默认会将其转为400(Bad Request)
![eb4eaf67fa6bd1c0a0287daafa527622.png](https://img-blog.csdnimg.cn/img_convert/eb4eaf67fa6bd1c0a0287daafa527622.png)
项目中 我们可以使用统一异常处理 返回错误信息
![c76aff25561f7bb792d7df4df9b6b975.png](https://img-blog.csdnimg.cn/img_convert/c76aff25561f7bb792d7df4df9b6b975.png)
2.2 参数一个个平铺到方法入参中的场景
此时必须在类上加@Validated注解,并且方法入参上加上校验相关的注解
![962794ea048d51c9242d690c2bd281ff.png](https://img-blog.csdnimg.cn/img_convert/962794ea048d51c9242d690c2bd281ff.png)
校验不通过,会抛出ConstraintViolationException异常,同样进行统一异常处理
![59a8868db7e63d2139941a93fe08f621.png](https://img-blog.csdnimg.cn/img_convert/59a8868db7e63d2139941a93fe08f621.png)
![fe1d11679496adc051b51e50aba43363.png](https://img-blog.csdnimg.cn/img_convert/fe1d11679496adc051b51e50aba43363.png)
2.3 表单传参 参数对象接收
参数对象前 加@Valid或@Validated注解就可以
![0176fca79f59ff707611be7fc1cf8d7a.png](https://img-blog.csdnimg.cn/img_convert/0176fca79f59ff707611be7fc1cf8d7a.png)
校验不通过会 抛出BindException异常,同样进行统一异常处理
![9a9b8daf6e7b95ae57c3fcbea93818bc.png](https://img-blog.csdnimg.cn/img_convert/9a9b8daf6e7b95ae57c3fcbea93818bc.png)
测试
![285726baf097f75d0383f69e65231efb.png](https://img-blog.csdnimg.cn/img_convert/285726baf097f75d0383f69e65231efb.png)
3 spring bean validation底层校验原理
3.1 json传参校验原理
sprIngmvc中 RequestResponseBodyMethodProcessor 会解析@RequesetBody注解标注的参数,封装参数对象的,显然校验逻辑会在这里
我们看看其resolveArgument方法
![282f5030473e6a59925c5c946b343a95.png](https://img-blog.csdnimg.cn/img_convert/282f5030473e6a59925c5c946b343a95.png)
![c1031e8137632b85b41552208744b825.png](https://img-blog.csdnimg.cn/img_convert/c1031e8137632b85b41552208744b825.png)
![5d2cdab0dddad6d758f85137d5b429b8.png](https://img-blog.csdnimg.cn/img_convert/5d2cdab0dddad6d758f85137d5b429b8.png)
最后调用Spring的Validator进行校验,而spring的Validator实现内部就是对hibernate validation的封装
3.2 方法参数平铺 校验原理
其原理是aop,因为在类上加了@Validated注解,启动时会被MethodValidationPostProcessor类进行切面
![2437b397bd0fda7a3517647344f72b30.png](https://img-blog.csdnimg.cn/img_convert/2437b397bd0fda7a3517647344f72b30.png)
最终在MethodValidationInterceptor类中进行处理
![c7c9c1e3c5e8fd6bc8ebd3e34903a2e7.png](https://img-blog.csdnimg.cn/img_convert/c7c9c1e3c5e8fd6bc8ebd3e34903a2e7.png)
3.3 对象接收参数 校验原理
ModelAttributeMethodProcessor类会处理 参数解析
看起resolveArgument方法
其中有段逻辑是进行参数校验的
![64073ba25855852f85dc990b5c6fd2e7.png](https://img-blog.csdnimg.cn/img_convert/64073ba25855852f85dc990b5c6fd2e7.png)
后面的逻辑和3.1一样了
3.4 总结
原理都是在调用controller方法前 进行参数校验,要不就是解析参数时,要不就是切面进行参数校验,底层还是封装的hibernate validation
4 spring bean validation分组功能
有时一个java bean对象 在多个方法中使用,但校验需求不一样,这时可以用 校验分组功能
比如 save方法 不要求User.id有值,而update方法要求有值
此时User对象可以这样写
![78180fe19e5e9454f42ce8abdfb225a4.png](https://img-blog.csdnimg.cn/img_convert/78180fe19e5e9454f42ce8abdfb225a4.png)
使用时,@Validate注解上加上分组值
![87fe414069707c8e0e8874c71ea753f2.png](https://img-blog.csdnimg.cn/img_convert/87fe414069707c8e0e8874c71ea753f2.png)
就可以实现上述需求了