Skip to content

Commit c7269fe

Browse files
committed
Align validation metadata handling in PayloadMethodArgumentResolver
Reuses ValidationAnnotationUtils which is slightly optimized for the detection of Spring's Validated annotation now, also to the benefit of common web scenarios. Closes gh-21852
1 parent 1e75041 commit c7269fe

File tree

4 files changed

+37
-32
lines changed

4 files changed

+37
-32
lines changed

spring-context/src/main/java/org/springframework/validation/annotation/ValidationAnnotationUtils.java

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,37 +26,46 @@
2626
* Mainly for internal use within the framework.
2727
*
2828
* @author Christoph Dreis
29+
* @author Juergen Hoeller
2930
* @since 5.3.7
3031
*/
3132
public abstract class ValidationAnnotationUtils {
3233

3334
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
3435

36+
3537
/**
3638
* Determine any validation hints by the given annotation.
37-
* <p>This implementation checks for {@code @jakarta.validation.Valid},
38-
* Spring's {@link org.springframework.validation.annotation.Validated},
39-
* and custom annotations whose name starts with "Valid".
39+
* <p>This implementation checks for Spring's
40+
* {@link org.springframework.validation.annotation.Validated},
41+
* {@code @jakarta.validation.Valid}, and custom annotations whose
42+
* name starts with "Valid" which may optionally declare validation
43+
* hints through the "value" attribute.
4044
* @param ann the annotation (potentially a validation annotation)
4145
* @return the validation hints to apply (possibly an empty array),
4246
* or {@code null} if this annotation does not trigger any validation
4347
*/
4448
@Nullable
4549
public static Object[] determineValidationHints(Annotation ann) {
50+
// Direct presence of @Validated ?
51+
if (ann instanceof Validated validated) {
52+
return validated.value();
53+
}
54+
// Direct presence of @Valid ?
4655
Class<? extends Annotation> annotationType = ann.annotationType();
47-
String annotationName = annotationType.getName();
48-
if ("jakarta.validation.Valid".equals(annotationName)) {
56+
if ("jakarta.validation.Valid".equals(annotationType.getName())) {
4957
return EMPTY_OBJECT_ARRAY;
5058
}
59+
// Meta presence of @Validated ?
5160
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
5261
if (validatedAnn != null) {
53-
Object hints = validatedAnn.value();
54-
return convertValidationHints(hints);
62+
return validatedAnn.value();
5563
}
64+
// Custom validation annotation ?
5665
if (annotationType.getSimpleName().startsWith("Valid")) {
57-
Object hints = AnnotationUtils.getValue(ann);
58-
return convertValidationHints(hints);
66+
return convertValidationHints(AnnotationUtils.getValue(ann));
5967
}
68+
// No validation triggered
6069
return null;
6170
}
6271

spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,8 +1309,8 @@ static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, Annotate
13091309
*/
13101310
public static boolean isSynthesizedAnnotation(@Nullable Annotation annotation) {
13111311
try {
1312-
return ((annotation != null) && Proxy.isProxyClass(annotation.getClass()) &&
1313-
(Proxy.getInvocationHandler(annotation) instanceof SynthesizedMergedAnnotationInvocationHandler));
1312+
return (annotation != null && Proxy.isProxyClass(annotation.getClass()) &&
1313+
Proxy.getInvocationHandler(annotation) instanceof SynthesizedMergedAnnotationInvocationHandler);
13141314
}
13151315
catch (SecurityException ex) {
13161316
// Security settings disallow reflective access to the InvocationHandler:

spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/reactive/PayloadMethodArgumentResolver.java

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
import org.springframework.core.ReactiveAdapter;
3434
import org.springframework.core.ReactiveAdapterRegistry;
3535
import org.springframework.core.ResolvableType;
36-
import org.springframework.core.annotation.AnnotationUtils;
3736
import org.springframework.core.codec.Decoder;
3837
import org.springframework.core.codec.DecodingException;
3938
import org.springframework.core.io.buffer.DataBuffer;
@@ -54,17 +53,17 @@
5453
import org.springframework.validation.BeanPropertyBindingResult;
5554
import org.springframework.validation.SmartValidator;
5655
import org.springframework.validation.Validator;
57-
import org.springframework.validation.annotation.Validated;
56+
import org.springframework.validation.annotation.ValidationAnnotationUtils;
5857

5958
/**
6059
* A resolver to extract and decode the payload of a message using a
61-
* {@link Decoder}, where the payload is expected to be a {@link Publisher} of
62-
* {@link DataBuffer DataBuffer}.
60+
* {@link Decoder}, where the payload is expected to be a {@link Publisher}
61+
* of {@link DataBuffer DataBuffer}.
6362
*
6463
* <p>Validation is applied if the method argument is annotated with
65-
* {@code @jakarta.validation.Valid} or
66-
* {@link org.springframework.validation.annotation.Validated}. Validation
67-
* failure results in an {@link MethodArgumentNotValidException}.
64+
* {@link org.springframework.validation.annotation.Validated} or
65+
* {@code @jakarta.validation.Valid}. Validation failure results in an
66+
* {@link MethodArgumentNotValidException}.
6867
*
6968
* <p>This resolver should be ordered last if {@link #useDefaultResolution} is
7069
* set to {@code true} since in that case it supports all types and does not
@@ -286,10 +285,8 @@ private Consumer<Object> getValidator(Message<?> message, MethodParameter parame
286285
return null;
287286
}
288287
for (Annotation ann : parameter.getParameterAnnotations()) {
289-
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
290-
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
291-
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
292-
Object[] validationHints = (hints instanceof Object[] objectHints ? objectHints : new Object[] {hints});
288+
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
289+
if (validationHints != null) {
293290
String name = Conventions.getVariableNameForParameter(parameter);
294291
return target -> {
295292
BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(target, name);

spring-messaging/src/main/java/org/springframework/messaging/handler/annotation/support/PayloadMethodArgumentResolver.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.Optional;
2121

2222
import org.springframework.core.MethodParameter;
23-
import org.springframework.core.annotation.AnnotationUtils;
2423
import org.springframework.lang.Nullable;
2524
import org.springframework.messaging.Message;
2625
import org.springframework.messaging.converter.MessageConversionException;
@@ -38,12 +37,16 @@
3837
import org.springframework.validation.ObjectError;
3938
import org.springframework.validation.SmartValidator;
4039
import org.springframework.validation.Validator;
41-
import org.springframework.validation.annotation.Validated;
40+
import org.springframework.validation.annotation.ValidationAnnotationUtils;
4241

4342
/**
4443
* A resolver to extract and convert the payload of a message using a
45-
* {@link MessageConverter}. It also validates the payload using a
46-
* {@link Validator} if the argument is annotated with a Validation annotation.
44+
* {@link MessageConverter}.
45+
*
46+
* <p>Validation is applied if the method argument is annotated with
47+
* {@link org.springframework.validation.annotation.Validated} or
48+
* {@code @jakarta.validation.Valid}. Validation failure results in an
49+
* {@link MethodArgumentNotValidException}.
4750
*
4851
* <p>This {@link HandlerMethodArgumentResolver} should be ordered last as it
4952
* supports all types and does not require the {@link Payload} annotation.
@@ -196,8 +199,6 @@ protected Class<?> resolveTargetClass(MethodParameter parameter, Message<?> mess
196199

197200
/**
198201
* Validate the payload if applicable.
199-
* <p>The default implementation checks for {@code @jakarta.validation.Valid},
200-
* Spring's {@link Validated}, and custom annotations whose name starts with "Valid".
201202
* @param message the currently processed message
202203
* @param parameter the method parameter
203204
* @param target the target payload object
@@ -208,10 +209,8 @@ protected void validate(Message<?> message, MethodParameter parameter, Object ta
208209
return;
209210
}
210211
for (Annotation ann : parameter.getParameterAnnotations()) {
211-
Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);
212-
if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {
213-
Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));
214-
Object[] validationHints = (hints instanceof Object[] objectHints ? objectHints : new Object[] {hints});
212+
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
213+
if (validationHints != null) {
215214
BeanPropertyBindingResult bindingResult =
216215
new BeanPropertyBindingResult(target, getParameterName(parameter));
217216
if (!ObjectUtils.isEmpty(validationHints) && this.validator instanceof SmartValidator sv) {

0 commit comments

Comments
 (0)