博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
springboot情操陶冶-web配置(七)
阅读量:4502 次
发布时间:2019-06-08

本文共 7622 字,大约阅读时间需要 25 分钟。

参数校验通常是OpenApi必做的操作,其会对不合法的输入做统一的校验以防止恶意的请求。本文则对参数校验这方面作下简单的分析

spring.factories

读者应该对此文件加以深刻的印象,很多springboot整合第三方插件的方式均是从此配置文件去读取的,本文关注下检验方面的东西。在相应的文件搜索validation关键字,最终定位至ValidationAutoConfiguration类,笔者这就针对此类作主要的分析

ValidationAutoConfiguration

优先看下其头上的注解

@Configuration@ConditionalOnClass(ExecutableValidator.class)@ConditionalOnResource(resources = "classpath:META-INF/services/javax.validation.spi.ValidationProvider")@Import(PrimaryDefaultValidatorPostProcessor.class)

使此类成功被注册的条件有两个,第一是当前环境下存在ExecutableValidator类,第二是当前类环境存在META-INF/services/javax.validation.spi.ValidationProvider文件。

通过查看maven依赖得知,其实springboot在引入starter-web板块便引入了hibernate-validator包,此包便满足了上述的两个要求。
笔者发现其也引入了PrimaryDefaultValidatorPostProcessor类,主要是判断当前的bean工厂是否已经包含了LocalValidatorFactoryBeanValidator对象,不影响大局。即使没有配置,下述的代码也是会注册的

@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) {        MethodValidationPostProcessor processor = new MethodValidationPostProcessor();        boolean proxyTargetClass = environment                .getProperty("spring.aop.proxy-target-class", Boolean.class, true);        processor.setProxyTargetClass(proxyTargetClass);        processor.setValidator(validator);        return processor;    }

通过查阅代码得知,使用注解式的校验方式是通过添加@Validated注解来实现的,但是其作用于参数上还是类上是有不同的操作逻辑的。笔者将之区分开,方便后续查阅。先附上@Validated注解源码

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Validated {    /**     * Specify one or more validation groups to apply to the validation step     * kicked off by this annotation.     * 

JSR-303 defines validation groups as custom annotations which an application declares * for the sole purpose of using them as type-safe group arguments, as implemented in * {@link org.springframework.validation.beanvalidation.SpringValidatorAdapter}. *

Other {@link org.springframework.validation.SmartValidator} implementations may * support class arguments in other ways as well. */ Class

[] value() default {};}

类级别的校验

@Validated作用于类上,其相关的处理逻辑便是由MethodValidationPostProcessor来实现的,笔者稍微看下关键源码方法afterPropertiesSet()

@Override    public void afterPropertiesSet() {        // 查找对应的类以及祖先类上是否含有@Validated注解        Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);        // 创建MethodValidationInterceptor处理类来处理具体的逻辑        this.advisor = new DefaultPointcutAdvisor(pointcut, createMethodValidationAdvice(this.validator));    }

上述的配置表明只要某个类上使用了@Validated注解,其相应的方法就会被校验相关的参数。笔者紧接着看下MethodValidationInterceptor#invoke()方法

@Override    @SuppressWarnings("unchecked")    public Object invoke(MethodInvocation invocation) throws Throwable {        // 读取相应方法上的@Validated的value属性,为空也是没问题的        Class
[] groups = determineValidationGroups(invocation); // Standard Bean Validation 1.1 API ExecutableValidator execVal = this.validator.forExecutables(); Method methodToValidate = invocation.getMethod(); Set
> result; try { // ①校验参数 result = execVal.validateParameters( invocation.getThis(), methodToValidate, invocation.getArguments(), groups); } catch (IllegalArgumentException ex) { // ②校验对应的桥接方法(兼容jdk1.5+后的泛型用法)的参数 methodToValidate = BridgeMethodResolver.findBridgedMethod( ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass())); result = execVal.validateParameters( invocation.getThis(), methodToValidate, invocation.getArguments(), groups); } if (!result.isEmpty()) { throw new ConstraintViolationException(result); } // ③校验对应的返回值 Object returnValue = invocation.proceed(); result = execVal.validateReturnValue(invocation.getThis(), methodToValidate, returnValue, groups); if (!result.isEmpty()) { throw new ConstraintViolationException(result); } return returnValue; }

只要类上使用了@Validated注解,则其下的所有方法都会被校验。

检验规则如下:参数返回值都会被校验,只要某一个没有通过,则会抛出ConstraintViolationException异常以示警告。
具体的参数校验属于hibernate-validator的范畴了,感兴趣的读者可自行分析~

参数级别的校验(推荐)

@Validated注解作用于方法的参数上,其有关的校验则是被springmvc的参数校验器处理的。笔者在ModelAttributeMethodProcessor#resolveArgument()方法中查找到了相应的蛛丝马迹,列出关键的代码

@Override    @Nullable    public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,            NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {        ....        Object attribute = null;        BindingResult bindingResult = null;        if (mavContainer.containsAttribute(name)) {            attribute = mavContainer.getModel().get(name);        }        else {            // Create attribute instance            try {                attribute = createAttribute(name, parameter, binderFactory, webRequest);            }            catch (BindException ex) {                .....            }        }        if (bindingResult == null) {            WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);            if (binder.getTarget() != null) {                if (!mavContainer.isBindingDisabled(name)) {                    bindRequestParameters(binder, webRequest);                }                // 就是这里                validateIfApplicable(binder, parameter);                if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {                    throw new BindException(binder.getBindingResult());                }            }            // Value type adaptation, also covering java.util.Optional            if (!parameter.getParameterType().isInstance(attribute)) {                attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);            }            bindingResult = binder.getBindingResult();        }        ....        return attribute;    }

我们继续看下其下的validateIfApplicable()方法

protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {        // 对参数上含有@Validated注解的进行校验器解析        Annotation[] annotations = parameter.getParameterAnnotations();        for (Annotation ann : annotations) {            Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);                        // 兼容@Valid注解方式            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;            }        }    }

上述的代码已经很简明概要了,笔者就不展开了。当然如果用户想要在出现异常的时候进行友好的返回,建议参考的异常机制文章便可迎刃而解

小结

参数的校验一般都是结合spring-context板块内的@Validated注解搭配hibernate的校验器便完成了相应的检测功能。

经过笔者的测试,发现在类上使用@Validated注释基本没啥用,还是会依赖在参数上添加@Validated注解方可生效。
如果大家对此有什么补充欢迎留言,在此希望此篇对大家有所帮助

转载于:https://www.cnblogs.com/question-sky/p/9984860.html

你可能感兴趣的文章
CallBack Function Python
查看>>
读书笔记-代码大全
查看>>
CentOS7为docker-ce配置阿里云镜像加速器
查看>>
groovy基本语法--JSON
查看>>
学习笔记2 Haspmap简述
查看>>
【AngularJs】 <br>换行显示成字符串
查看>>
Angular2 父子组件通信方式
查看>>
window.location.href问题,点击,跳转到首页
查看>>
判断一个 int 向量里是否有相同的数(1)
查看>>
数据库设置
查看>>
Python基础知识之3——运算符与表达式
查看>>
串口通信类,WPF
查看>>
UIView下使用Animation控制动画
查看>>
TP之空操作及View模块
查看>>
shiro学习笔记:授权管理
查看>>
Java 使用正则表达式取出图片地址以及跳转的链接地址,来判断死链(一)
查看>>
代理delegate、NSNotification、KVO在开发中的抉择
查看>>
剑指offer--二叉搜索树的后序遍历序列
查看>>
Selenium学习第一章:搭建测试环境
查看>>
SASS笔记
查看>>