设为首页 加入收藏

TOP

使用 SpringBoot 进行优雅的数据验证(六)
2023-08-26 21:11:14 】 浏览:112
Tags:使用 SpringBoot
或者是负数,入参验证不会检测出来。

为了能够进行嵌套验证,必须手动在 Item 实体的 props 字段上明确指出这个字段里面的实体也要进行验证。由于@Validated 不能用在成员属性(字段)上,但是@Valid 能加在成员属性(字段)上,而且@Valid 类注解上也说明了它支持嵌套验证功能,那么我们能够推断出:@Valid 加在方法参数时并不能够自动进行嵌套验证,而是用在需要嵌套验证类的相应字段上,来配合方法参数上@Validated 或@Valid 来进行嵌套验证。

我们修改 Item 类如下所示:

public class Item {

    @NotNull(message = "id不能为空")
    @Min(value = 1, message = "id必须为正整数")
    private Long id;

    @Valid // 嵌套验证必须用@Valid
    @NotNull(message = "props不能为空")
    @Size(min = 1, message = "props至少要有一个自定义属性")
    private List<Prop> props;

}

然后我们在 ItemController 的 addItem 函数上再使用@Validated 或者@Valid,就能对 Item 的入参进行嵌套验证。此时 Item 里面的 props 如果含有 Prop 的相应字段为空的情况,Spring Validation 框架就会检测出来,bindingResult 就会记录相应的错误。

Spring Validation 原理简析

现在我们来简单分析下 Spring 校验功能的原理。

方法级别的参数校验实现原理

所谓的方法级别的校验就是指将@NotNull 和@NotEmpty 这些约束直接加在方法的参数上的。

比如

@GetMapping("/getUser")
@ResponseBody
public R getUser(@NotNull(message = "userId 不能为空") Integer userId){
//
}

或者

@Validated
@Service
public class ValidatorService {

    private static final Logger logger = LoggerFactory.getLogger(ValidatorService.class);

    public String show(@NotNull(message = "不能为空") @Min(value = 18, message = "最小18") String age) {
    	logger.info("age = {}", age);
    	return age;
    }

}

都属于方法级别的校验。这种方式可用于任何 Spring Bean 的方法上,比如 Controller/Service 等。

其底层实现原理就是 AOP,具体来说是通过 MethodValidationPostProcessor 动态注册 AOP 切面,然后使用 MethodValidationInterceptor 对切点方法织入增强。

public class MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessorimplements InitializingBean {
    @Override
    public void afterPropertiesSet() {
        //为所有`@Validated`标注的 Bean 创建切面
        Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
        //创建 Advisor 进行增强
        this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));
    }

    //创建Advice,本质就是一个方法拦截器
    protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
        return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
    }

}

接着看一下 MethodValidationInterceptor:

public class MethodValidationInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        //无需增强的方法,直接跳过
        if (isFactoryBeanMetadataMethod(invocation.getMethod())) {
            return invocation.proceed();
        }
        //获取分组信息
        Class[] groups = determineva lidationGroups(invocation);
        Executableva lidator execVal = this.validator.forExecutables();
        Method methodToValidate = invocation.getMethod();
        Set<constraintviolation> result;
        try {
            //方法入参校验,最终还是委托给 Hibernate Validator 来校验
            result = execVal.validateParameters(
            invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
        } catch (IllegalArgumentException ex) {
            ...
        }
        //有异常直接抛出
        if (!result.isEmpty()) {
            throw new ConstraintViolationException(result);
        }
        //真正的方法调用
        Object returnValue = invocation.proceed();
        //对返回值做校验,最终还是委托给 Hibernate Validator 来校验
        result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups);
        //有异常直接抛出
        if (!result.isEmpty()) {
            throw new ConstraintViolationException(result);
        }
        return returnValue;
    }
}

DTO 级别的校验

@PostMappin
首页 上一页 3 4 5 6 7 下一页 尾页 6/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇记录一次解决数据库连接池连接泄.. 下一篇Tomcat+Eclipse乱码问题解决方法

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目