Skip to content

Commit

Permalink
Explicit initialization of shouldValidate flags
Browse files Browse the repository at this point in the history
Closes gh-32007
  • Loading branch information
rstoyanchev committed Jan 10, 2024
1 parent f6e5290 commit e7eaaad
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -72,6 +72,7 @@ public class HandlerMethod extends AnnotatedMethod {
/** Logger that is available to subclasses. */
protected static final Log logger = LogFactory.getLog(HandlerMethod.class);


private final Object bean;

@Nullable
Expand Down Expand Up @@ -116,8 +117,8 @@ protected HandlerMethod(Object bean, Method method, @Nullable MessageSource mess
this.beanFactory = null;
this.messageSource = messageSource;
this.beanType = ClassUtils.getUserClass(bean);
this.validateArguments = MethodValidationInitializer.checkArguments(this.beanType, getMethodParameters());
this.validateReturnValue = MethodValidationInitializer.checkReturnValue(this.beanType, getBridgedMethod());
this.validateArguments = false;
this.validateReturnValue = false;
evaluateResponseStatus();
this.description = initDescription(this.beanType, method);
}
Expand All @@ -132,8 +133,8 @@ public HandlerMethod(Object bean, String methodName, Class<?>... parameterTypes)
this.beanFactory = null;
this.messageSource = null;
this.beanType = ClassUtils.getUserClass(bean);
this.validateArguments = MethodValidationInitializer.checkArguments(this.beanType, getMethodParameters());
this.validateReturnValue = MethodValidationInitializer.checkReturnValue(this.beanType, getBridgedMethod());
this.validateArguments = false;
this.validateReturnValue = false;
evaluateResponseStatus();
this.description = initDescription(this.beanType, getMethod());
}
Expand Down Expand Up @@ -166,8 +167,8 @@ public HandlerMethod(
throw new IllegalStateException("Cannot resolve bean type for bean with name '" + beanName + "'");
}
this.beanType = ClassUtils.getUserClass(beanType);
this.validateArguments = MethodValidationInitializer.checkArguments(this.beanType, getMethodParameters());
this.validateReturnValue = MethodValidationInitializer.checkReturnValue(this.beanType, getBridgedMethod());
this.validateArguments = false;
this.validateReturnValue = false;
evaluateResponseStatus();
this.description = initDescription(this.beanType, method);
}
Expand All @@ -176,31 +177,24 @@ public HandlerMethod(
* Copy constructor for use in subclasses.
*/
protected HandlerMethod(HandlerMethod handlerMethod) {
super(handlerMethod);
this.bean = handlerMethod.bean;
this.beanFactory = handlerMethod.beanFactory;
this.messageSource = handlerMethod.messageSource;
this.beanType = handlerMethod.beanType;
this.validateArguments = handlerMethod.validateArguments;
this.validateReturnValue = handlerMethod.validateReturnValue;
this.responseStatus = handlerMethod.responseStatus;
this.responseStatusReason = handlerMethod.responseStatusReason;
this.description = handlerMethod.description;
this.resolvedFromHandlerMethod = handlerMethod.resolvedFromHandlerMethod;
this(handlerMethod, null, false);
}

/**
* Re-create HandlerMethod with the resolved handler.
* Re-create HandlerMethod with additional input.
*/
private HandlerMethod(HandlerMethod handlerMethod, Object handler) {
private HandlerMethod(HandlerMethod handlerMethod, @Nullable Object handler, boolean initValidateFlags) {
super(handlerMethod);
Assert.notNull(handler, "Handler object is required");
this.bean = handler;
this.bean = (handler != null ? handler : handlerMethod.bean);
this.beanFactory = handlerMethod.beanFactory;
this.messageSource = handlerMethod.messageSource;
this.beanType = handlerMethod.beanType;
this.validateArguments = handlerMethod.validateArguments;
this.validateReturnValue = handlerMethod.validateReturnValue;
this.validateArguments = (initValidateFlags ?
MethodValidationInitializer.checkArguments(this.beanType, getMethodParameters()) :
handlerMethod.validateArguments);
this.validateReturnValue = (initValidateFlags ?
MethodValidationInitializer.checkReturnValue(this.beanType, getBridgedMethod()) :
handlerMethod.validateReturnValue);
this.responseStatus = handlerMethod.responseStatus;
this.responseStatusReason = handlerMethod.responseStatusReason;
this.resolvedFromHandlerMethod = handlerMethod;
Expand Down Expand Up @@ -313,6 +307,15 @@ public HandlerMethod getResolvedFromHandlerMethod() {
return this.resolvedFromHandlerMethod;
}

/**
* Re-create the HandlerMethod and initialize
* {@link #shouldValidateArguments()} and {@link #shouldValidateReturnValue()}.
* @since 6.1.3
*/
public HandlerMethod createWithValidateFlags() {
return new HandlerMethod(this, null, true);
}

/**
* If the provided instance contains a bean name rather than an object instance,
* the bean name is resolved before a {@link HandlerMethod} is created and returned.
Expand All @@ -323,7 +326,8 @@ public HandlerMethod createWithResolvedBean() {
Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
handler = this.beanFactory.getBean(beanName);
}
return new HandlerMethod(this, handler);
Assert.notNull(handler, "No handler instance");
return new HandlerMethod(this, handler, false);
}

/**
Expand Down Expand Up @@ -392,14 +396,17 @@ protected String formatInvokeError(String text, Object[] args) {
*/
private static class MethodValidationInitializer {

private static final boolean BEAN_VALIDATION_PRESENT =
ClassUtils.isPresent("jakarta.validation.Validator", HandlerMethod.class.getClassLoader());

private static final Predicate<MergedAnnotation<? extends Annotation>> CONSTRAINT_PREDICATE =
MergedAnnotationPredicates.typeIn("jakarta.validation.Constraint");

private static final Predicate<MergedAnnotation<? extends Annotation>> VALID_PREDICATE =
MergedAnnotationPredicates.typeIn("jakarta.validation.Valid");

public static boolean checkArguments(Class<?> beanType, MethodParameter[] parameters) {
if (AnnotationUtils.findAnnotation(beanType, Validated.class) == null) {
if (BEAN_VALIDATION_PRESENT && AnnotationUtils.findAnnotation(beanType, Validated.class) == null) {
for (MethodParameter param : parameters) {
MergedAnnotations merged = MergedAnnotations.from(param.getParameterAnnotations());
if (merged.stream().anyMatch(CONSTRAINT_PREDICATE)) {
Expand All @@ -419,7 +426,7 @@ public static boolean checkArguments(Class<?> beanType, MethodParameter[] parame
}

public static boolean checkReturnValue(Class<?> beanType, Method method) {
if (AnnotationUtils.findAnnotation(beanType, Validated.class) == null) {
if (BEAN_VALIDATION_PRESENT && AnnotationUtils.findAnnotation(beanType, Validated.class) == null) {
MergedAnnotations merged = MergedAnnotations.from(method, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
return merged.stream().anyMatch(CONSTRAINT_PREDICATE.or(VALID_PREDICATE));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private static void testValidateReturnValue(Object target, List<String> methodNa

private static HandlerMethod getHandlerMethod(Object target, String methodName) {
Method method = ClassUtils.getMethod(target.getClass(), methodName, (Class<?>[]) null);
return new HandlerMethod(target, method);
return new HandlerMethod(target, method).createWithValidateFlags();
}


Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -524,6 +524,9 @@ public void register(T mapping, Object handler, Method method) {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);

// Enable method validation, if applicable
handlerMethod = handlerMethod.createWithValidateFlags();

Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -339,7 +339,7 @@ void springValidator() throws Exception {
@SuppressWarnings("unchecked")
private static <T> HandlerMethod handlerMethod(T controller, Consumer<T> mockCallConsumer) {
Method method = ResolvableMethod.on((Class<T>) controller.getClass()).mockCall(mockCallConsumer).method();
return new HandlerMethod(controller, method);
return new HandlerMethod(controller, method).createWithValidateFlags();
}

private static MockServerHttpRequest.BodyBuilder request() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -632,6 +632,9 @@ public void register(T mapping, Object handler, Method method) {
HandlerMethod handlerMethod = createHandlerMethod(handler, method);
validateMethodMapping(handlerMethod, mapping);

// Enable method validation, if applicable
handlerMethod = handlerMethod.createWithValidateFlags();

Set<String> directPaths = AbstractHandlerMethodMapping.this.getDirectPaths(mapping);
for (String path : directPaths) {
this.pathLookup.add(path, mapping);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -301,7 +301,7 @@ void springValidator() throws Exception {
@SuppressWarnings("unchecked")
private static <T> HandlerMethod handlerMethod(T controller, Consumer<T> mockCallConsumer) {
Method method = ResolvableMethod.on((Class<T>) controller.getClass()).mockCall(mockCallConsumer).method();
return new HandlerMethod(controller, method);
return new HandlerMethod(controller, method).createWithValidateFlags();
}

@SuppressWarnings("SameParameterValue")
Expand Down

0 comments on commit e7eaaad

Please sign in to comment.