Skip to content

Commit

Permalink
Align validation metadata handling in PayloadMethodArgumentResolver
Browse files Browse the repository at this point in the history
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
  • Loading branch information
jhoeller committed Aug 16, 2023
1 parent 1e75041 commit c7269fe
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 32 deletions.
Expand Up @@ -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.
* <p>This implementation checks for {@code @jakarta.validation.Valid},
* Spring's {@link org.springframework.validation.annotation.Validated},
* and custom annotations whose name starts with "Valid".
* <p>This implementation checks for Spring's
* {@link org.springframework.validation.annotation.Validated},
* {@code @jakarta.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 validated) {
return validated.value();
}
// Direct presence of @Valid ?
Class<? extends Annotation> annotationType = ann.annotationType();
String annotationName = annotationType.getName();
if ("jakarta.validation.Valid".equals(annotationName)) {
if ("jakarta.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;
}

Expand Down
Expand Up @@ -1309,8 +1309,8 @@ static Annotation[] synthesizeAnnotationArray(Annotation[] annotations, Annotate
*/
public static boolean isSynthesizedAnnotation(@Nullable Annotation annotation) {
try {
return ((annotation != null) && Proxy.isProxyClass(annotation.getClass()) &&
(Proxy.getInvocationHandler(annotation) instanceof SynthesizedMergedAnnotationInvocationHandler));
return (annotation != null && Proxy.isProxyClass(annotation.getClass()) &&
Proxy.getInvocationHandler(annotation) instanceof SynthesizedMergedAnnotationInvocationHandler);
}
catch (SecurityException ex) {
// Security settings disallow reflective access to the InvocationHandler:
Expand Down
Expand Up @@ -33,7 +33,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;
Expand All @@ -54,17 +53,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}.
*
* <p>Validation is applied if the method argument is annotated with
* {@code @jakarta.validation.Valid} or
* {@link org.springframework.validation.annotation.Validated}. Validation
* failure results in an {@link MethodArgumentNotValidException}.
* {@link org.springframework.validation.annotation.Validated} or
* {@code @jakarta.validation.Valid}. Validation failure results in an
* {@link MethodArgumentNotValidException}.
*
* <p>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
Expand Down Expand Up @@ -286,10 +285,8 @@ private Consumer<Object> 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[] objectHints ? objectHints : new Object[] {hints});
Object[] validationHints = ValidationAnnotationUtils.determineValidationHints(ann);
if (validationHints != null) {
String name = Conventions.getVariableNameForParameter(parameter);
return target -> {
BeanPropertyBindingResult bindingResult = new BeanPropertyBindingResult(target, name);
Expand Down
Expand Up @@ -20,7 +20,6 @@
import java.util.Optional;

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;
Expand All @@ -38,12 +37,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}.
*
* <p>Validation is applied if the method argument is annotated with
* {@link org.springframework.validation.annotation.Validated} or
* {@code @jakarta.validation.Valid}. Validation failure results in an
* {@link MethodArgumentNotValidException}.
*
* <p>This {@link HandlerMethodArgumentResolver} should be ordered last as it
* supports all types and does not require the {@link Payload} annotation.
Expand Down Expand Up @@ -196,8 +199,6 @@ protected Class<?> resolveTargetClass(MethodParameter parameter, Message<?> mess

/**
* Validate the payload if applicable.
* <p>The default implementation checks for {@code @jakarta.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
Expand All @@ -208,10 +209,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[] objectHints ? objectHints : 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 sv) {
Expand Down

0 comments on commit c7269fe

Please sign in to comment.