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 196f6fc6c74e..0842b812ba0e 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 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. @@ -26,37 +26,46 @@ * Mainly for internal use within the framework. * * @author Christoph Dreis + * @author Juergen Hoeller * @since 5.3.7 */ public abstract class ValidationAnnotationUtils { private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; + /** * 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". + *

This implementation checks for Spring's + * {@link org.springframework.validation.annotation.Validated}, + * {@code @javax.validation.Valid}, and custom annotations whose + * name starts with "Valid" which may optionally declare validation + * hints through the "value" attribute. * @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 */ @Nullable public static Object[] determineValidationHints(Annotation ann) { + // Direct presence of @Validated ? + if (ann instanceof Validated) { + return ((Validated) ann).value(); + } + // Direct presence of @Valid ? Class annotationType = ann.annotationType(); - String annotationName = annotationType.getName(); - if ("javax.validation.Valid".equals(annotationName)) { + if ("javax.validation.Valid".equals(annotationType.getName())) { return EMPTY_OBJECT_ARRAY; } + // Meta presence of @Validated ? Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class); if (validatedAnn != null) { - Object hints = validatedAnn.value(); - return convertValidationHints(hints); + return validatedAnn.value(); } + // Custom validation annotation ? if (annotationType.getSimpleName().startsWith("Valid")) { - Object hints = AnnotationUtils.getValue(ann); - return convertValidationHints(hints); + return convertValidationHints(AnnotationUtils.getValue(ann)); } + // No validation triggered return null; } diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/PayloadMethodArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/PayloadMethodArgumentResolver.java index 3e3c930f05e5..4912e672d873 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/PayloadMethodArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/PayloadMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 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. @@ -34,7 +34,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.Decoder; import org.springframework.core.codec.DecodingException; import org.springframework.core.io.buffer.DataBuffer; @@ -55,17 +54,17 @@ import org.springframework.validation.BeanPropertyBindingResult; import org.springframework.validation.SmartValidator; import org.springframework.validation.Validator; -import org.springframework.validation.annotation.Validated; +import org.springframework.validation.annotation.ValidationAnnotationUtils; /** * A resolver to extract and decode the payload of a message using a - * {@link Decoder}, where the payload is expected to be a {@link Publisher} of - * {@link DataBuffer DataBuffer}. + * {@link Decoder}, where the payload is expected to be a {@link Publisher} + * of {@link DataBuffer DataBuffer}. * *

Validation is applied if the method argument is annotated with - * {@code @javax.validation.Valid} or - * {@link org.springframework.validation.annotation.Validated}. Validation - * failure results in an {@link MethodArgumentNotValidException}. + * {@link org.springframework.validation.annotation.Validated} or + * {@code @javax.validation.Valid}. Validation failure results in an + * {@link MethodArgumentNotValidException}. * *

This resolver should be ordered last if {@link #useDefaultResolution} is * set to {@code true} since in that case it supports all types and does not @@ -287,10 +286,8 @@ private Consumer getValidator(Message message, MethodParameter parame return null; } 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)); - Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); + Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann); + if (validationHints != null) { String name = Conventions.getVariableNameForParameter(parameter); return target -> { BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(target, name); diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolver.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolver.java index 103a3c1b8ec3..3ef7b86c189d 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolver.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolver.java @@ -19,7 +19,6 @@ import java.lang.annotation.Annotation; import org.springframework.core.MethodParameter; -import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.messaging.converter.MessageConversionException; @@ -36,12 +35,16 @@ import org.springframework.validation.ObjectError; import org.springframework.validation.SmartValidator; import org.springframework.validation.Validator; -import org.springframework.validation.annotation.Validated; +import org.springframework.validation.annotation.ValidationAnnotationUtils; /** * A resolver to extract and convert the payload of a message using a - * {@link MessageConverter}. It also validates the payload using a - * {@link Validator} if the argument is annotated with a Validation annotation. + * {@link MessageConverter}. + * + *

Validation is applied if the method argument is annotated with + * {@link org.springframework.validation.annotation.Validated} or + * {@code @javax.validation.Valid}. Validation failure results in an + * {@link MethodArgumentNotValidException}. * *

This {@link HandlerMethodArgumentResolver} should be ordered last as it * supports all types and does not require the {@link Payload} annotation. @@ -190,8 +193,6 @@ protected Class resolveTargetClass(MethodParameter parameter, Message mess /** * Validate the payload if applicable. - *

The default implementation checks for {@code @javax.validation.Valid}, - * Spring's {@link Validated}, and custom annotations whose name starts with "Valid". * @param message the currently processed message * @param parameter the method parameter * @param target the target payload object @@ -202,10 +203,8 @@ protected void validate(Message message, MethodParameter parameter, Object ta return; } 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)); - Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints}); + Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann); + if (validationHints != null) { BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(target, getParameterName(parameter)); if (!ObjectUtils.isEmpty(validationHints) && this.validator instanceof SmartValidator) {