From 56eadacb2f17bc581287834d0c8b8b9358559083 Mon Sep 17 00:00:00 2001 From: Christoph Dreis Date: Sun, 11 Apr 2021 09:35:58 +0200 Subject: [PATCH 1/4] Avoid exceptions when evaluating validation hints Prior to this commit, evaluating validation hints for @javax.validation.Valid caused exceptions being raised when getting the value of this annotation, which does not exist. Bypassing AnnotationUtils.getValue() in those cases can improve performance by avoiding the cost incurred by raising exceptions. --- .../validation/ValidationUtils.java | 42 ++++++++++++++++++- .../ModelAttributeMethodProcessor.java | 29 ++----------- ...AbstractMessageReaderArgumentResolver.java | 10 ++--- .../ModelAttributeMethodArgumentResolver.java | 18 +++----- ...essageConverterMethodArgumentResolver.java | 9 ++-- 5 files changed, 57 insertions(+), 51 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java b/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java index 8ed9b569304c..36e5bf00b33c 100644 --- a/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java +++ b/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,17 @@ package org.springframework.validation; +import java.lang.annotation.Annotation; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; +import org.springframework.validation.annotation.Validated; /** * Utility class offering convenient methods for invoking a {@link Validator} @@ -255,4 +259,40 @@ public static void rejectIfEmptyOrWhitespace( } } + /** + * Determine any validation hints by the given annotation. + *

This implementation checks for {@code @javax.validation.Valid}, + * Spring's {@link org.springframework.validation.annotation.Validated}, + * and custom annotations whose name starts with "Valid". + * @param ann the annotation (potentially a validation annotation) + * @return the validation hints to apply (possibly an empty array), + * or {@code null} if this annotation does not trigger any validation + * @since 5.3.6 + */ + @Nullable + public static Object[] determineValidationHints(Annotation ann) { + String annotationName = ann.annotationType().getName(); + if ("javax.validation.Valid".equals(annotationName)) { + return new Object[0]; + } + Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); + if (validatedAnn != null) { + Object hints = validatedAnn.value(); + return convertValidationHints(hints); + } + int offset = annotationName.lastIndexOf('.'); + if (annotationName.startsWith("Valid", Math.max(offset, 0))) { + Object hints = AnnotationUtils.getValue(ann); + return convertValidationHints(hints); + } + return null; + } + + private static Object[] convertValidationHints(@Nullable Object hints) { + if (hints == null) { + return new Object[0]; + } + return (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints}); + } + } diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java index c09d9ec75348..c1a4dbb287b8 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,6 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.TypeMismatchException; import org.springframework.core.MethodParameter; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -45,8 +44,8 @@ import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.validation.SmartValidator; +import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -362,7 +361,7 @@ else if (StringUtils.startsWithIgnoreCase(request.getHeader("Content-Type"), "mu */ protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { for (Annotation ann : parameter.getParameterAnnotations()) { - Object[] validationHints = determineValidationHints(ann); + Object[] validationHints = ValidationUtils.determineValidationHints(ann); if (validationHints != null) { binder.validate(validationHints); break; @@ -388,7 +387,7 @@ protected void validateValueIfApplicable(WebDataBinder binder, MethodParameter p Class targetType, String fieldName, @Nullable Object value) { for (Annotation ann : parameter.getParameterAnnotations()) { - Object[] validationHints = determineValidationHints(ann); + Object[] validationHints = ValidationUtils.determineValidationHints(ann); if (validationHints != null) { for (Validator validator : binder.getValidators()) { if (validator instanceof SmartValidator) { @@ -406,26 +405,6 @@ protected void validateValueIfApplicable(WebDataBinder binder, MethodParameter p } } - /** - * Determine any validation triggered by the given annotation. - * @param ann the annotation (potentially a validation annotation) - * @return the validation hints to apply (possibly an empty array), - * or {@code null} if this annotation does not trigger any validation - * @since 5.1 - */ - @Nullable - private Object[] determineValidationHints(Annotation ann) { - Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); - if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) { - Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann)); - if (hints == null) { - return new Object[0]; - } - return (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); - } - return null; - } - /** * Whether to raise a fatal bind exception on validation errors. *

The default implementation delegates to {@link #isBindExceptionRequired(MethodParameter)}. diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java index c278ca059711..6b4ee0d9ca74 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java @@ -31,7 +31,6 @@ import org.springframework.core.ReactiveAdapter; import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.codec.DecodingException; import org.springframework.core.codec.Hints; import org.springframework.core.io.buffer.DataBuffer; @@ -44,8 +43,8 @@ import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.support.WebExchangeBindException; import org.springframework.web.bind.support.WebExchangeDataBinder; import org.springframework.web.reactive.BindingContext; @@ -240,10 +239,9 @@ private ServerWebInputException handleMissingBody(MethodParameter parameter) { private Object[] extractValidationHints(MethodParameter parameter) { Annotation[] annotations = parameter.getParameterAnnotations(); for (Annotation ann : annotations) { - Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); - if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) { - Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann)); - return (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); + Object[] hints = ValidationUtils.determineValidationHints(ann); + if (hints != null) { + return hints; } } return null; diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java index 645ae8e19e41..259894bc8a36 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,14 +30,13 @@ import org.springframework.core.ReactiveAdapter; import org.springframework.core.ReactiveAdapterRegistry; import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; import org.springframework.ui.Model; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; -import org.springframework.validation.annotation.Validated; +import org.springframework.validation.ValidationUtils; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.support.WebExchangeBindException; import org.springframework.web.bind.support.WebExchangeDataBinder; @@ -270,16 +269,9 @@ private boolean hasErrorsArgument(MethodParameter parameter) { private void validateIfApplicable(WebExchangeDataBinder binder, MethodParameter parameter) { for (Annotation ann : parameter.getParameterAnnotations()) { - Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); - if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) { - Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann)); - if (hints != null) { - Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); - binder.validate(validationHints); - } - else { - binder.validate(); - } + Object[] validationHints = ValidationUtils.determineValidationHints(ann); + if (validationHints != null) { + binder.validate(validationHints); } } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java index 6e96a085974a..975307c60cf8 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java @@ -36,7 +36,6 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ResolvableType; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.log.LogFormatUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpInputMessage; @@ -52,7 +51,7 @@ import org.springframework.util.Assert; import org.springframework.util.StreamUtils; import org.springframework.validation.Errors; -import org.springframework.validation.annotation.Validated; +import org.springframework.validation.ValidationUtils; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.context.request.NativeWebRequest; @@ -241,10 +240,8 @@ protected ServletServerHttpRequest createInputMessage(NativeWebRequest webReques protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { Annotation[] annotations = parameter.getParameterAnnotations(); for (Annotation ann : annotations) { - Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); - 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}); + Object[] validationHints = ValidationUtils.determineValidationHints(ann); + if (validationHints != null) { binder.validate(validationHints); break; } From f2beb2ada651c30588ce8b7323afcc503d73ac96 Mon Sep 17 00:00:00 2001 From: Christoph Dreis Date: Wed, 21 Apr 2021 12:33:11 +0200 Subject: [PATCH 2/4] Extract logic into ValidationAnnotationUtils --- .../validation/ValidationUtils.java | 40 ----------- .../annotation/ValidationAnnotationUtils.java | 69 +++++++++++++++++++ .../ModelAttributeMethodProcessor.java | 6 +- ...AbstractMessageReaderArgumentResolver.java | 4 +- .../ModelAttributeMethodArgumentResolver.java | 4 +- ...essageConverterMethodArgumentResolver.java | 4 +- 6 files changed, 78 insertions(+), 49 deletions(-) create mode 100644 spring-context/src/main/java/org/springframework/validation/annotation/ValidationAnnotationUtils.java diff --git a/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java b/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java index 36e5bf00b33c..d884d8512218 100644 --- a/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java +++ b/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java @@ -16,17 +16,13 @@ package org.springframework.validation; -import java.lang.annotation.Annotation; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; -import org.springframework.validation.annotation.Validated; /** * Utility class offering convenient methods for invoking a {@link Validator} @@ -259,40 +255,4 @@ public static void rejectIfEmptyOrWhitespace( } } - /** - * Determine any validation hints by the given annotation. - *

This implementation checks for {@code @javax.validation.Valid}, - * Spring's {@link org.springframework.validation.annotation.Validated}, - * and custom annotations whose name starts with "Valid". - * @param ann the annotation (potentially a validation annotation) - * @return the validation hints to apply (possibly an empty array), - * or {@code null} if this annotation does not trigger any validation - * @since 5.3.6 - */ - @Nullable - public static Object[] determineValidationHints(Annotation ann) { - String annotationName = ann.annotationType().getName(); - if ("javax.validation.Valid".equals(annotationName)) { - return new Object[0]; - } - Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); - if (validatedAnn != null) { - Object hints = validatedAnn.value(); - return convertValidationHints(hints); - } - int offset = annotationName.lastIndexOf('.'); - if (annotationName.startsWith("Valid", Math.max(offset, 0))) { - Object hints = AnnotationUtils.getValue(ann); - return convertValidationHints(hints); - } - return null; - } - - private static Object[] convertValidationHints(@Nullable Object hints) { - if (hints == null) { - return new Object[0]; - } - return (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints}); - } - } diff --git a/spring-context/src/main/java/org/springframework/validation/annotation/ValidationAnnotationUtils.java b/spring-context/src/main/java/org/springframework/validation/annotation/ValidationAnnotationUtils.java new file mode 100644 index 000000000000..c3d86b7265ee --- /dev/null +++ b/spring-context/src/main/java/org/springframework/validation/annotation/ValidationAnnotationUtils.java @@ -0,0 +1,69 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.validation.annotation; + +import java.lang.annotation.Annotation; + +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.lang.Nullable; + +/** + * Utility class for handling validation annotations. + * Mainly for internal use within the framework. + * + * @author Christoph Dreis + * @since 5.3.7 + */ +public abstract class ValidationAnnotationUtils { + + /** + * Determine any validation hints by the given annotation. + *

This implementation checks for {@code @javax.validation.Valid}, + * Spring's {@link org.springframework.validation.annotation.Validated}, + * and custom annotations whose name starts with "Valid". + * @param ann the annotation (potentially a validation annotation) + * @return the validation hints to apply (possibly an empty array), + * or {@code null} if this annotation does not trigger any validation + * @since 5.3.7 + */ + @Nullable + public static Object[] determineValidationHints(Annotation ann) { + String annotationName = ann.annotationType().getName(); + if ("javax.validation.Valid".equals(annotationName)) { + return new Object[0]; + } + Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); + if (validatedAnn != null) { + Object hints = validatedAnn.value(); + return convertValidationHints(hints); + } + int offset = annotationName.lastIndexOf('.'); + if (annotationName.startsWith("Valid", Math.max(offset, 0))) { + Object hints = AnnotationUtils.getValue(ann); + return convertValidationHints(hints); + } + return null; + } + + private static Object[] convertValidationHints(@Nullable Object hints) { + if (hints == null) { + return new Object[0]; + } + return (hints instanceof Object[] ? (Object[]) hints : new Object[]{hints}); + } + +} diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java index c1a4dbb287b8..2f4cafc781e6 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java @@ -44,8 +44,8 @@ import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; import org.springframework.validation.SmartValidator; -import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; +import org.springframework.validation.annotation.ValidationAnnotationUtils; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.support.WebDataBinderFactory; @@ -361,7 +361,7 @@ else if (StringUtils.startsWithIgnoreCase(request.getHeader("Content-Type"), "mu */ protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { for (Annotation ann : parameter.getParameterAnnotations()) { - Object[] validationHints = ValidationUtils.determineValidationHints(ann); + Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann); if (validationHints != null) { binder.validate(validationHints); break; @@ -387,7 +387,7 @@ protected void validateValueIfApplicable(WebDataBinder binder, MethodParameter p Class targetType, String fieldName, @Nullable Object value) { for (Annotation ann : parameter.getParameterAnnotations()) { - Object[] validationHints = ValidationUtils.determineValidationHints(ann); + Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann); if (validationHints != null) { for (Validator validator : binder.getValidators()) { if (validator instanceof SmartValidator) { diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java index 6b4ee0d9ca74..07a7e70f4861 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java @@ -43,8 +43,8 @@ import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.validation.ValidationUtils; import org.springframework.validation.Validator; +import org.springframework.validation.annotation.ValidationAnnotationUtils; import org.springframework.web.bind.support.WebExchangeBindException; import org.springframework.web.bind.support.WebExchangeDataBinder; import org.springframework.web.reactive.BindingContext; @@ -239,7 +239,7 @@ private ServerWebInputException handleMissingBody(MethodParameter parameter) { private Object[] extractValidationHints(MethodParameter parameter) { Annotation[] annotations = parameter.getParameterAnnotations(); for (Annotation ann : annotations) { - Object[] hints = ValidationUtils.determineValidationHints(ann); + Object[] hints = ValidationAnnotationUtils.determineValidationHints(ann); if (hints != null) { return hints; } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java index 259894bc8a36..4da0f0d53f86 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java @@ -36,7 +36,7 @@ import org.springframework.util.ClassUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.Errors; -import org.springframework.validation.ValidationUtils; +import org.springframework.validation.annotation.ValidationAnnotationUtils; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.support.WebExchangeBindException; import org.springframework.web.bind.support.WebExchangeDataBinder; @@ -269,7 +269,7 @@ private boolean hasErrorsArgument(MethodParameter parameter) { private void validateIfApplicable(WebExchangeDataBinder binder, MethodParameter parameter) { for (Annotation ann : parameter.getParameterAnnotations()) { - Object[] validationHints = ValidationUtils.determineValidationHints(ann); + Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann); if (validationHints != null) { binder.validate(validationHints); } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java index 975307c60cf8..1dbc559e2ccf 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodArgumentResolver.java @@ -51,7 +51,7 @@ import org.springframework.util.Assert; import org.springframework.util.StreamUtils; import org.springframework.validation.Errors; -import org.springframework.validation.ValidationUtils; +import org.springframework.validation.annotation.ValidationAnnotationUtils; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.context.request.NativeWebRequest; @@ -240,7 +240,7 @@ protected ServletServerHttpRequest createInputMessage(NativeWebRequest webReques protected void validateIfApplicable(WebDataBinder binder, MethodParameter parameter) { Annotation[] annotations = parameter.getParameterAnnotations(); for (Annotation ann : annotations) { - Object[] validationHints = ValidationUtils.determineValidationHints(ann); + Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann); if (validationHints != null) { binder.validate(validationHints); break; From 9f3c9da15db4444cea4091fdb38429efbaf427af Mon Sep 17 00:00:00 2001 From: Christoph Dreis Date: Wed, 21 Apr 2021 12:55:57 +0200 Subject: [PATCH 3/4] Restore previous copyright --- .../java/org/springframework/validation/ValidationUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java b/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java index d884d8512218..8ed9b569304c 100644 --- a/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java +++ b/spring-context/src/main/java/org/springframework/validation/ValidationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 38cdc092c75dd901e5d44c3d0826aaf1df5dc2c9 Mon Sep 17 00:00:00 2001 From: Christoph Dreis Date: Thu, 22 Apr 2021 11:02:54 +0200 Subject: [PATCH 4/4] Fix handling of custom @Valid annotations --- .../validation/annotation/ValidationAnnotationUtils.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/annotation/ValidationAnnotationUtils.java b/spring-context/src/main/java/org/springframework/validation/annotation/ValidationAnnotationUtils.java index c3d86b7265ee..05a265247bb7 100644 --- a/spring-context/src/main/java/org/springframework/validation/annotation/ValidationAnnotationUtils.java +++ b/spring-context/src/main/java/org/springframework/validation/annotation/ValidationAnnotationUtils.java @@ -42,7 +42,8 @@ public abstract class ValidationAnnotationUtils { */ @Nullable public static Object[] determineValidationHints(Annotation ann) { - String annotationName = ann.annotationType().getName(); + Class annotationType = ann.annotationType(); + String annotationName = annotationType.getName(); if ("javax.validation.Valid".equals(annotationName)) { return new Object[0]; } @@ -51,8 +52,7 @@ public static Object[] determineValidationHints(Annotation ann) { Object hints = validatedAnn.value(); return convertValidationHints(hints); } - int offset = annotationName.lastIndexOf('.'); - if (annotationName.startsWith("Valid", Math.max(offset, 0))) { + if (annotationType.getSimpleName().startsWith("Valid")) { Object hints = AnnotationUtils.getValue(ann); return convertValidationHints(hints); }