/**
* <pre>
*
* 总结:
* 1. 在SpringBoot中,参数为非复杂对象类型,默认情况下,在参数解析的时候不会进行参数校验,因为这这些简单类型不支持Bean Validation规范,而参数为复杂对象的时候,才会使用校验器对该对象进行字段校验
*
*
* 简单参数如何进行校验呢? 除非手动调用Validator的API,否则就需要Spirng接入,Spring会提供一个{@link ValidationAutoConfiguration#methodValidationPostProcessor}后置处理器
* 给类中添加了@Validated注解的类生成代理对象,并且在方法执行的过程中进行拦截{@link ValidationAutoConfiguration.MethodValidationInterceptor},对参数进行校验
* 该代理对象还可以对返回值进行校验
*
* 在复杂对象参数解析的过程中,会自动对对象字段进行校验,校验出来的结果之后,如果对象参数后面紧挨的下一个参数为Error(BindingResult)类型,则不会抛出异常,则会把校验信息存入Error(BindingResult)中
* 这是在springmvc源码中体现的
*
* 2. 在SpringMVC中,对于非复杂对象的类型参数,是不会进行参数校验的,即使添加了@Validated或者@Valid注解,因为这这些简单类型不支持Bean Validation规范
* 但是,会对复杂的对象(JavaBean)进行参数校验,前提是参数中标记了@Validated或者@Valid注解,SpringMVC会自动对对象进行参数校验
* 在复杂对象(JavaBean)参数解析的过程中,校验出来的结果之后,如果对象参数后面紧挨的下一个参数为Error(BindingResult)类型,如果校验失败也不会抛出异常,而是把校验信息存入Error(BindingResult)中
* 默认不会给标注了@Validated的类生成代理对象,因为不会有自动配置,如果要完成简单参数的校验,我们有两种做法
* 1. 使用SpringBoot类似的做法,给容器中注册MethodValidationPostProcessor后置处理器,该处理器会注册一个拦截器,拦截器用于校验参数的validation注解信息,既可以校验参数,也可以校验返回值
* 2. 可以使用AOP切面,拦截方法参数,使用Validator进行校验
*
* @Bean
* public static MethodValidationPostProcessor methodValidationPostProcessor(Environment environment,@Lazy Validator validator) {
* // 或者直接创建MethodValidationPostProcessor对象
* MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
* boolean proxyTargetClass = environment.getProperty("spring.aop.proxy-target-class", Boolean.class, true);
* processor.setProxyTargetClass(proxyTargetClass);
* processor.setValidator(validator);
* return processor;
* }
*
* 3. 扩展,配置Validator
* @Configuration
* public class LuckConfig implements WebMvcConfigurer {
* // 手动配置Validator
* public LocalValidatorFactoryBean localValidatorFactoryBean() {
* return new LocalValidatorFactoryBean();
* }
* // 通过WebMvcConfigurer配置
* @Override
* public Validator getValidator() {
* return new LocalValidatorFactoryBean();
* }
* }
*
* <p>
* 注意: 非JavaBean字段参数的下个参数为BindingResult的话,会抛出异常,因为只有在JavaBean对象校验的时候,会添加一个"BindingResult.对象名"的标记
* 因此在参数解析的时候,解析到BindingResult,则会从Model对象中找以"BindingResult."开头的数据,如果没有保存"BindingResult."的标记,则会抛出异常
* {@link ErrorsMethodArgumentResolver}
* 因此,只有在JavaBean参数校验的时候,才能使用BindingResult参数接收错误信息
* </pre>
*/
// 参数解析器,用于解析参数为Errors(BindingResult参数)
public class ErrorsMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return Errors.class.isAssignableFrom(paramType);
}
@Override
@Nullable
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 获取运行中的Model数据
ModelMap model = mavContainer.getModel();
// 获取model中存入的所有数据,是否存在"BindingResult.class.getName() + "."标记
// 该key是标记该对象是JavaBean对象,进行了校验操作
String lastKey = CollectionUtils.lastElement(model.keySet());
if (lastKey != null && lastKey.startsWith(BindingResult.MODEL_KEY_PREFIX)) {
return model.get(lastKey);
}
// 如果不包含"BindingResult.class.getName() + "."标记,则抛出异常
throw new IllegalStateException(
"An Errors/BindingResult argument is expected to be declared immediately after " +
"the model attribute, the @RequestBody or the @RequestPart arguments " +
"to which they apply: " + parameter.getMethod());
}
}
// 参数解析器,用于解析JavaBean参数,或者标有@ModelAttribute注解的参数
public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResolver, HandlerMethodReturnValueHandler {
public boolean supportsParameter(MethodParameter parameter) {
// 存在@ModelAttribute注解,或者参数指定了不是必须的,并且是JavaBean类型
return (parameter.hasParameterAnnotation(ModelAttribute.class) || (this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())));
}
@Nullable
public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
// 获取参数名称
String name = ModelFactory.getNameForParameter(parameter);
// 获取注解信息
ModelAttribute ann = parameter.getParameterAnnotation(ModelAttribute.class);
if (ann != null) {
// 设置当前参数是否需要绑定
mavContainer.setBinding(name, ann.binding());
}
// 参数对象
Object attribute = null;
// 异常结果对象
BindingResult bindingResult = null;
// 如果在Model中存在该参数值
if (mavContainer.containsAttribute(name)) {
// 直接从Model中获取数据
attribute = mavContainer.getModel().get(name);
} else {
// 如果Model中没有存入该对象,则创建对象的实例
try {
attribute = createAttribute(name, parameter, binderFactory, webRequest);
} catch (BindException ex) {
// 创建对象失败,是否需要抛出绑定异常
// 当参数校验失败,判断当前参数的下一个参数为BindingResult,如果不是,则抛出异常
if (isBindExceptionRequired(parameter)) {
//无BindingResult参数,直接抛出异常
throw ex;
}
// 保存校验的结果对象
bindingResult = ex.getBindingResult();
}
}
// 如果创建对象成功
if (bindingResult == null) {
// 创建对象属性值绑定器
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name);
// 如果存在绑定的对象
if (binder.getTarget() != null) {
// 如果没有禁用参数绑定
if (!mavContainer.isBindingDisabled(name)) {
// 进行参数绑定,就是将参数从request中获取,进行类型转换,然后设置到对象中,都是由binder对象完成
this.bindRequestParameters(binder, webRequest);
}
// 参数校验@Validated和@Valid注解
this.validateIfApplicable(binder, parameter);
// 校验结果存在binder的bindingResult中,并且校验失败之后,是否必须抛出绑定异常
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {
throw new BindException(binder.getBindingResult());
}
}
// 如果参数解析到的参数值和实际类型不一致,进行类型转换
if (!parameter.getParameterType().isInstance(attribute)) {
attribute = binder.convertIfNecessary(binder.getTarget(), parameter.getParameterType(), parameter);
}
// 获取数据绑定的结果
bindingResult = binder.getBindingResult();
}
// 获取绑定结果中的数据
Map<String, Object> bindingResultModel = bindingResult.getModel() {
// 标记两个字段,一个是对象信息,一个是绑定信息
Map<String, Object> model = new LinkedHashMap<>(2);
// 从名称到目标对象的映射。
model.put(getObjectName(), getTarget());
// 错误实例,即使没有错误
model.put(MODEL_KEY_PREFIX + getObjectName(), this);
return model;
}
// 删除之前绑定的数据
mavContainer.removeAttributes(bindingResultModel);
// 将新数据重新保存
mavContainer.addAllAttributes(bindingResultModel);
// 返回绑定好的参数对象
return attribute;
}
// 校验失败之后,是否必须抛出绑定异常
protected boolean isBindExceptionRequired(MethodParameter parameter) {
// 获取参数的位置
int i = parameter.getParameterIndex();
// 获取到参数的类型
Class<?>[] paramTypes = parameter.getExecutable().getParameterTypes();
// 如果当前参数的下一个参数为Errors(BindingResult),则不会抛出异常,否则抛出异常
boolean hasBindingResult = (paramTypes.length > (i + 1) && Errors.class.isAssignableFrom(paramTypes[i + 1]));
return !hasBindingResult;
}
// 参数校验,是否存在@Valid***注解信息
protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) {
// 获取参数的注解
for (Annotation ann : parameter.getParameterAnnotations()) {
// 校验@Valid***注解
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann) {
Class<? extends Annotation> annotationType = ann.annotationType();
String annotationName = annotationType.getName();
// 如果注解为@Valid,没有需要校验的分组
if ("javax.validation.Valid".equals(annotationName)) {
return EMPTY_OBJECT_ARRAY;
}
// 注解为Validated,获取注解的value值,获取校验的分组信息
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
if (validatedAnn != null) {
Object hints = validatedAnn.value();
// 如果不为空,返回指定的分组,否则返回EMPTY_OBJECT_ARRAY
return this.convertValidationHints(hints);
}
// 如果都不是上面两者,如果注解类型为Valid开头的注解,或者对应的值
if (annotationType.getSimpleName().startsWith("Valid")) {
Object hints = AnnotationUtils.getValue(ann);
// 如果不为空,返回指定的分组,否则返回EMPTY_OBJECT_ARRAY
return convertValidationHints(hints);
}
return null;
}
// 如果存在@Valid***注解,则不为null
if (validationHints != null) {
// 进行参数校验
binder.validate(validationHints);
break;
}
}
}
class WebDataBinder {
// 进行分组校验,并将结果
public void validate(Object... validationHints) {
// 获取需要校验的对象
Object target = this.getTarget();
// 从Binder对象中获取BindingResult,如果没有,创建一个保存到当前Binder对象中,用于保存校验结果
BindingResult bindingResult = this.getBindingResult();
// 遍历所有的校验器,这个校验器是从@EnableMvc中,注入的RequestMappingHandlerAdapter设置的一个ConfigurableWebBindingInitializer对象
// 该ConfigurableWebBindingInitializer对象中保存Spring中保存的类型转换器和参数转换器,也是通过@EnableMvc导入的WebMvcConfigurationSupport一起使用@Bean注入的
// 然后在WebDataBinderFactory创建WebDataBinder对象的时候,指定ConfigurableWebBindingInitializer该对象的initBinder方法,然后将ConfigurableWebBindingInitializer的数据都设置导WebDataBinder对象中
for (Validator validator : this.getValidators()) {
// 如果需要进行分组校验
if (!ObjectUtils.isEmpty(validationHints) && validator instanceof SmartValidator) {
// 调用Validator校验器开始校验
/**
* {@link LocalValidatorFactoryBean#validate}
*/
((SmartValidator) validator).validate(target, bindingResult, validationHints);
}
// 普通的校验器
else if (validator != null) {
/**
* {@link LocalValidatorFactoryBean#validate}
*/
validator.validate(target, bindingResult);
}
}
}
}
}
public class LocalValidatorFactoryBean extends SpringValidatorAdapter implements SmartValidator, Validator {
// 代理的Validator
private Validator targetValidator;
// 内置的属性名
static {
internalAnnotationAttributes.add("message");
internalAnnotationAttributes.add("groups");
internalAnnotationAttributes.add("payload");
}
// 设置需要代理的Validator
public void setTargetValidator(Validator targetValidator) {
this.targetValidator = targetValidator;
}
// 初始化操作
public void afterPropertiesSet() {
// 获取对Validator工厂的配置对象
Configuration<?> configuration = Validation.byDefaultProvider().configure();
ConstraintValidatorFactory targetConstraintValidatorFactory = this.constraintValidatorFactory;
// 对Validator工厂进行配置
if (targetConstraintValidatorFactory == null && this.applicationContext != null) {
targetConstraintValidatorFactory = new SpringConstraintValidatorFactory(this.applicationContext.getAutowireCapableBeanFactory());
}
if (targetConstraintValidatorFactory != null) {
configuration.constraintValidatorFactory(targetConstraintValidatorFactory);
}
if (this.parameterNameDiscoverer != null) {
configureParameterNameProvider(this.parameterNameDiscoverer, configuration);
}
// 构建Validator工厂
this.validatorFactory = configuration.buildValidatorFactory();
// 创建Validator,并保存
this.setTargetValidator(((ValidatorFactoryImpl) this.validatorFactory).getValidator());
}
// 分组验证整个对象
@Override
public void validate(Object target, Errors errors, Object... validationHints) {
// 如果存在代理的Validator,在上面LocalValidatorFactoryBean创建过,通过ValidatorFactoryImpl创建的
if (this.targetValidator != null) {
// 将提供的信息进行过滤,只过滤Class类型指定的分组
Class<?>[] groups = asValidationGroups(validationHints);
// 调用Hibernate提供的validator校验
Set<ConstraintViolation<Object>> violations = this.targetValidator.validate(target, groups);
// 处理校验结果,将Validator校验的结果保存到Errors(BindingResult)对象中
processConstraintViolations(violations, errors);
}
}
// 验证对象的某个字段的值
@Override
public void validateValue(Class<?> targetType, String fieldName, @Nullable Object value, Errors errors, Object... validationHints) {
// 和validate方法,这个事调用验证某个字段的api
if (this.targetValidator != null) {
// 处理校验结果,将Validator校验的结果保存到Errors(BindingResult)对象中
processConstraintViolations(this.targetValidator.validateValue((Class) targetType, fieldName, value, asValidationGroups(validationHints)), errors);
}
}
}
// 在spring.factories中,配置了org.springframework.context.ApplicationListener = org.springframework.boot.autoconfigure.BackgroundPreinitializer
// 该类是一个监听器,处理ApplicationEnvironmentPreparedEvent事件,环境预处理完成的回调事件
@Order(LoggingApplicationListener.DEFAULT_ORDER + 1)
public class BackgroundPreinitializer implements ApplicationListener<SpringApplicationEvent> {
public void onApplicationEvent(SpringApplicationEvent event) {
// 确定是第一次被初始化
if (event instanceof ApplicationEnvironmentPreparedEvent && preinitializationStarted.compareAndSet(false, true)) {
// 初始化一些配置信息
this.performPreinitialization();
}
}
private void performPreinitialization() {
// 启动新线程,执行下面这些初始化器的run方法
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
// 初始化类型转换器
runSafely(new ConversionServiceInitializer());
// 初始化Validator校验器
runSafely(new ValidationInitializer());
// 注册各种各样的消息转换器
runSafely(new MessageConverterInitializer());
// 注册ObjectMapper
runSafely(new JacksonInitializer());
// 初始化字符集
runSafely(new CharsetInitializer());
}
public void runSafely(Runnable runnable) {
runnable.run();
}
}, "background-preinit");
thread.start();
}
class ValidationInitializer implements Runnable {
// 这里只是为了初始化,触发类加载等等配置,并没有做什么实际性的东西
@Override
public void run() {
// 调用javax.validation.Validation的创建工厂的方法
// 下面两行代码与 Validation.buildDefaultValidatorFactory()方法完全一样
Configuration<?> configuration = Validation.byDefaultProvider().configure();
ValidatorFactory factory = configuration.buildValidatorFactory();
// 通过工厂获取
Validator validator = factory.getValidator();
}
}
}
// Hibernate实现了javax.validator.ValidatorFactory,class org.hibernate.validator.internal.engine.ValidatorFactoryImpl
public class ValidatorFactoryImpl implements ValidatorFactory {
@Override
public Validator getValidator() {
ConstraintValidatorFactory validatorFactory = constraintCreationContext.getConstraintValidatorManager().getDefaultConstraintValidatorFactory();
// 创建校验器
return this.createValidator(validatorFactory, constraintCreationContext, validatorFactoryScopedContext, methodValidationConfiguration);
}
// 创建校验器
public Validator createValidator(ConstraintValidatorFactory constraintValidatorFactory, ConstraintCreationContext constraintCreationContext, ValidatorFactoryScopedContext validatorFactoryScopedContext, MethodValidationConfiguration methodValidationConfiguration) {
// 获取到校验对象的元数据管理器
BeanMetaDataManager beanMetaDataManager = beanMetaDataManagers.computeIfAbsent(
new BeanMetaDataManagerKey(validatorFactoryScopedContext.getParameterNameProvider(),
constraintCreationContext.getValueExtractorManager(), methodValidationConfiguration),
key -> new BeanMetaDataManagerImpl(constraintCreationContext, executableHelper,
validatorFactoryScopedContext.getParameterNameProvider(), javaBeanHelper,
beanMetadataClassNormalizer, validationOrderGenerator, buildMetaDataProviders(),
methodValidationConfiguration)
);
// 返回Validator的实现类ValidatorImpl
return new ValidatorImpl(constraintValidatorFactory, beanMetaDataManager, constraintCreationContext.getValueExtractorManager(), constraintCreationContext.getConstraintValidatorManager(), validationOrderGenerator, validatorFactoryScopedContext);
}
}
class ValidatorImpl implements Validator, ExecutableValidator {
}
// 在springboot的自动配置中
@AutoConfiguration
@ConditionalOnClass(ExecutableValidator.class)
@ConditionalOnResource(resources = "classpath:META-INF/services/javax.validation.spi.ValidationProvider")
@Import(PrimaryDefaultValidatorPostProcessor.class)
public class ValidationAutoConfiguration {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean(Validator.class)
public static LocalValidatorFactoryBean defaultValidator(ApplicationContext applicationContext) {
// 会注册一个LocalValidatorFactoryBean,它本身是一个Validator
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
return factoryBean;
}
@Bean
@ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
public static FilteredMethodValidationPostProcessor methodValidationPostProcessor(Environment environment, @Lazy Validator validator, ObjectProvider<MethodValidationExcludeFilter> excludeFilters) {
// 拦截@Validated方法的后置处理器
FilteredMethodValidationPostProcessor processor = new FilteredMethodValidationPostProcessor(excludeFilters.orderedStream());
// 获取生成代理对象的类型,CGLIB还是JDK
boolean proxyTargetClass = environment.getProperty("spring.aop.proxy-target-class", Boolean.class, true);
processor.setProxyTargetClass(proxyTargetClass);
// 设置校验器
processor.setValidator(validator);
return processor;
}
public static class FilteredMethodValidationPostProcessor extends MethodValidationPostProcessor extends AbstractBeanFactoryAwareAdvisingPostProcessor {
// 校验的注解类型
private Class<? extends Annotation> validatedAnnotationType = Validated.class;
// 校验器
private Validator validator;
// 要生成代理对象的拦截器
protected Advisor advisor;
@Override
public void afterPropertiesSet() {
// 创建一个切面,因为存在Validated注解的类或者方法,需要生成代理对象
Pointcut pointcut = new AnnotationMatchingPointcut(this.validatedAnnotationType, true);
// 创建一个通知,拦截器
Advice advice = createMethodValidationAdvice(this.validator);
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}
protected Advice createMethodValidationAdvice(@Nullable Validator validator) {
return (validator != null ? new MethodValidationInterceptor(validator) : new MethodValidationInterceptor());
}
// 预处理代理工厂
@Override
protected ProxyFactory prepareProxyFactory(Object bean, String beanName) {
if (this.beanFactory != null) {
// 暴露目标对象,将目标对象按照指定的key保存到BeanDefinition中
AutoProxyUtils.exposeTargetClass(this.beanFactory, beanName, bean.getClass());
}
// 创建代理工厂
ProxyFactory proxyFactory = super.prepareProxyFactory(bean, beanName) {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 设置需要代理的对象
proxyFactory.setTarget(bean);
return proxyFactory;
}
// 是否使用CBlib代理
if (!proxyFactory.isProxyTargetClass() && this.beanFactory != null && AutoProxyUtils.shouldProxyTargetClass(this.beanFactory, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
return proxyFactory;
}
// 当前类MethodValidationPostProcessor就是为了
public Object postProcessAfterInitialization(Object bean, String beanName) {
// 如果当前没有拦截器,或者该bean本身是一个特殊的接口AopInfrastructureBean(标记该类为AOP的Bean)
if (this.advisor == null || bean instanceof AopInfrastructureBean) {
return bean;
}
// 该Bean是一个代理工厂,创建代理对象的工厂类
if (bean instanceof Advised) {
Advised advised = (Advised) bean;
if (!advised.isFrozen() && isEligible(AopUtils.getTargetClass(bean))) {
// 将我们的当前类需要应用的切面添加到现有代理的Advisor链中
if (this.beforeExistingAdvisors) {
advised.addAdvisor(0, this.advisor);
} else {
advised.addAdvisor(this.advisor);
}
return bean;
}
}
// 该bean是否可以应用上当前类需要应用的切面
if (this.isEligible(bean, beanName)) {
// 当前切面可以作用于该Bean对象,创建代理对象
ProxyFactory proxyFactory = this.prepareProxyFactory(bean, beanName);
// 没有设置使用CGLIB
if (!proxyFactory.isProxyTargetClass()) {
// 计算需要代理的接口
this.evaluateProxyInterfaces(bean.getClass(), proxyFactory);
}
// 添加切面
proxyFactory.addAdvisor(this.advisor);
// 自定义代理工厂
this.customizeProxyFactory(proxyFactory);
// 创建代理对象
return proxyFactory.getProxy(classLoader);
}
// 不需要代理直接返回
return bean;
}
}
public static class MethodValidationInterceptor implements MethodInterceptor {
// 校验器
private final Validator validator;
public Object invoke(MethodInvocation invocation) throws Throwable {
// 避免在FactoryBean.getObjectType/isSingleton上调用验证器
if (this.isFactoryBeanMetadataMethod(invocation.getMethod())) {
return invocation.proceed();
}
// 确定校验对象需要校验的分组
Class<?>[] groups = this.determineValidationGroups(invocation);
// 标准Bean验证1.1 API
// Validator本身只提供对象或者对象属性的校验,对于校验方法,它并没有提供API
// 而是提供了一个ExecutableValidator对于方法的校验器,该对象可以校验方法参数,方法返回值,构造参数
ExecutableValidator execVal = this.validator.forExecutables();
// 获取到需要校验的方法
Method methodToValidate = invocation.getMethod();
// 校验的结果
Set<ConstraintViolation<Object>> result;
// 校验的对象
Object target = invocation.getThis();
// 校验方法参数
result = execVal.validateParameters(target, methodToValidate, invocation.getArguments(), groups);
// 如果参数校验不同过,抛出异常
if (!result.isEmpty()) {
throw new ConstraintViolationException(result);
}
// 执行目标方法
Object returnValue = invocation.proceed();
// 再校验返回值
result = execVal.validateReturnValue(target, methodToValidate, returnValue, groups);
// 如果返回值校验失败,抛出异常
if (!result.isEmpty()) {
throw new ConstraintViolationException(result);
}
return returnValue;
}
// 确定校验对象需要校验的分组
protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
// 找方法中的Validated注解
Validated validatedAnn = AnnotationUtils.findAnnotation(invocation.getMethod(), Validated.class);
// 如果方法中不存在
if (validatedAnn == null) {
// 找类中的注解
Object target = invocation.getThis();
validatedAnn = AnnotationUtils.findAnnotation(target.getClass(), Validated.class);
}
// 返回注解中的分组的值
return (validatedAnn != null ? validatedAnn.value() : new Class<?>[0]);
}
}
}