diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/DefaultMethodValidator.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/DefaultMethodValidator.java
index 6b69851b8a..1e937661a0 100644
--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/DefaultMethodValidator.java
+++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/DefaultMethodValidator.java
@@ -18,6 +18,7 @@ package org.springframework.validation.beanvalidation;
import java.lang.reflect.Method;
+import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
/**
@@ -44,31 +45,45 @@ public class DefaultMethodValidator implements MethodValidator {
}
@Override
- public void validateArguments(Object target, Method method, Object[] arguments, Class>[] groups) {
- MethodValidationResult result = this.adapter.validateMethodArguments(target, method, arguments, groups);
- handleArgumentsResult(target, method, arguments, groups, result);
+ public void validateArguments(
+ Object target, Method method, @Nullable MethodParameter[] parameters, Object[] arguments,
+ Class>[] groups) {
+
+ handleArgumentsValidationResult(target, method, arguments, groups,
+ this.adapter.validateMethodArguments(target, method, parameters, arguments, groups));
+ }
+
+ public void validateReturnValue(
+ Object target, Method method, @Nullable MethodParameter returnType, @Nullable Object returnValue,
+ Class>[] groups) {
+
+ handleReturnValueValidationResult(target, method, returnValue, groups,
+ this.adapter.validateMethodReturnValue(target, method, returnType, returnValue, groups));
}
/**
* Subclasses can override this to handle the result of argument validation.
* By default, {@link MethodValidationResult#throwIfViolationsPresent()} is called.
+ * @param bean the target Object for method invocation
+ * @param method the target method
+ * @param arguments the candidate argument values to validate
+ * @param groups groups for validation determined via
*/
- protected void handleArgumentsResult(
+ protected void handleArgumentsValidationResult(
Object bean, Method method, Object[] arguments, Class>[] groups, MethodValidationResult result) {
result.throwIfViolationsPresent();
}
- public void validateReturnValue(Object target, Method method, @Nullable Object returnValue, Class>[] groups) {
- MethodValidationResult result = this.adapter.validateMethodReturnValue(target, method, returnValue, groups);
- handleReturnValueResult(target, method, returnValue, groups, result);
- }
-
/**
* Subclasses can override this to handle the result of return value validation.
* By default, {@link MethodValidationResult#throwIfViolationsPresent()} is called.
+ * @param bean the target Object for method invocation
+ * @param method the target method
+ * @param returnValue the return value to validate
+ * @param groups groups for validation determined via
*/
- protected void handleReturnValueResult(
+ protected void handleReturnValueValidationResult(
Object bean, Method method, @Nullable Object returnValue, Class>[] groups, MethodValidationResult result) {
result.throwIfViolationsPresent();
diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationAdapter.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationAdapter.java
index 5bc071405c..7ea1936672 100644
--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationAdapter.java
+++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationAdapter.java
@@ -176,8 +176,8 @@ public class MethodValidationAdapter {
/**
* Use this method determine the validation groups to pass into
- * {@link #validateMethodArguments(Object, Method, Object[], Class[])} and
- * {@link #validateMethodReturnValue(Object, Method, Object, Class[])}.
+ * {@link #validateMethodArguments(Object, Method, MethodParameter[], Object[], Class[])} and
+ * {@link #validateMethodReturnValue(Object, Method, MethodParameter, Object, Class[])}.
*
Default are the validation groups as specified in the {@link Validated}
* annotation on the method, or on the containing target class of the method,
* or for an AOP proxy without a target (with all behavior in advisors), also
@@ -208,7 +208,8 @@ public class MethodValidationAdapter {
* Validate the given method arguments and return the result of validation.
* @param target the target Object
* @param method the target method
- * @param arguments candidate arguments for a method invocation
+ * @param parameters the parameters, if already created and available
+ * @param arguments the candidate argument values to validate
* @param groups groups for validation determined via
* {@link #determineValidationGroups(Object, Method)}
* @return a result with {@link ConstraintViolation violations} and
@@ -216,7 +217,8 @@ public class MethodValidationAdapter {
* in case there are no violations
*/
public MethodValidationResult validateMethodArguments(
- Object target, Method method, Object[] arguments, Class>[] groups) {
+ Object target, Method method, @Nullable MethodParameter[] parameters, Object[] arguments,
+ Class>[] groups) {
ExecutableValidator execVal = this.validator.get().forExecutables();
Set> result;
@@ -231,14 +233,18 @@ public class MethodValidationAdapter {
result = execVal.validateParameters(target, bridgedMethod, arguments, groups);
}
return (result.isEmpty() ? EMPTY_RESULT :
- createException(target, method, result, i -> arguments[i], false));
+ createException(target, method, result,
+ i -> parameters != null ? parameters[i] : new MethodParameter(method, i),
+ i -> arguments[i],
+ false));
}
/**
* Validate the given return value and return the result of validation.
* @param target the target Object
* @param method the target method
- * @param returnValue value returned from invoking the target method
+ * @param returnType the return parameter, if already created and available
+ * @param returnValue the return value to validate
* @param groups groups for validation determined via
* {@link #determineValidationGroups(Object, Method)}
* @return a result with {@link ConstraintViolation violations} and
@@ -246,16 +252,22 @@ public class MethodValidationAdapter {
* in case there are no violations
*/
public MethodValidationResult validateMethodReturnValue(
- Object target, Method method, @Nullable Object returnValue, Class>[] groups) {
+ Object target, Method method, @Nullable MethodParameter returnType, @Nullable Object returnValue,
+ Class>[] groups) {
ExecutableValidator execVal = this.validator.get().forExecutables();
Set> result = execVal.validateReturnValue(target, method, returnValue, groups);
- return (result.isEmpty() ? EMPTY_RESULT : createException(target, method, result, i -> returnValue, true));
+ return (result.isEmpty() ? EMPTY_RESULT :
+ createException(target, method, result,
+ i -> returnType != null ? returnType : new MethodParameter(method, -1),
+ i -> returnValue,
+ true));
}
private MethodValidationException createException(
Object target, Method method, Set> violations,
- Function argumentFunction, boolean forReturnValue) {
+ Function parameterFunction, Function argumentFunction,
+ boolean forReturnValue) {
Map parameterViolations = new LinkedHashMap<>();
Map cascadedViolations = new LinkedHashMap<>();
@@ -267,10 +279,11 @@ public class MethodValidationAdapter {
MethodParameter parameter;
if (node.getKind().equals(ElementKind.PARAMETER)) {
- parameter = new MethodParameter(method, node.as(Path.ParameterNode.class).getParameterIndex());
+ int index = node.as(Path.ParameterNode.class).getParameterIndex();
+ parameter = parameterFunction.apply(index);
}
else if (node.getKind().equals(ElementKind.RETURN_VALUE)) {
- parameter = new MethodParameter(method, -1);
+ parameter = parameterFunction.apply(-1);
}
else {
continue;
diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
index e37b688230..c596463a52 100644
--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
+++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
@@ -104,12 +104,12 @@ public class MethodValidationInterceptor implements MethodInterceptor {
Method method = invocation.getMethod();
Class>[] groups = determineValidationGroups(invocation);
- this.delegate.validateMethodArguments(target, method, invocation.getArguments(), groups)
+ this.delegate.validateMethodArguments(target, method, null, invocation.getArguments(), groups)
.throwIfViolationsPresent();
Object returnValue = invocation.proceed();
- this.delegate.validateMethodReturnValue(target, method, returnValue, groups)
+ this.delegate.validateMethodReturnValue(target, method, null, returnValue, groups)
.throwIfViolationsPresent();
return returnValue;
diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidator.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidator.java
index 3109aad494..d549422a5e 100644
--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidator.java
+++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidator.java
@@ -18,6 +18,7 @@ package org.springframework.validation.beanvalidation;
import java.lang.reflect.Method;
+import org.springframework.core.MethodParameter;
import org.springframework.lang.Nullable;
/**
@@ -35,8 +36,8 @@ public interface MethodValidator {
/**
* Use this method determine the validation groups to pass into
- * {@link #validateArguments(Object, Method, Object[], Class[])} and
- * {@link #validateReturnValue(Object, Method, Object, Class[])}.
+ * {@link #validateArguments(Object, Method, MethodParameter[], Object[], Class[])} and
+ * {@link #validateReturnValue(Object, Method, MethodParameter, Object, Class[])}.
* @param target the target Object
* @param method the target method
* @return the applicable validation groups as a {@code Class} array
@@ -48,24 +49,30 @@ public interface MethodValidator {
* Validate the given method arguments and return the result of validation.
* @param target the target Object
* @param method the target method
- * @param arguments candidate arguments for a method invocation
+ * @param parameters the parameters, if already created and available
+ * @param arguments the candidate argument values to validate
* @param groups groups for validation determined via
* {@link #determineValidationGroups(Object, Method)}
* @throws MethodValidationException should be raised in case of validation
* errors unless the implementation handles those errors otherwise (e.g.
* by injecting {@code BindingResult} into the method).
*/
- void validateArguments(Object target, Method method, Object[] arguments, Class>[] groups);
+ void validateArguments(
+ Object target, Method method, @Nullable MethodParameter[] parameters, Object[] arguments,
+ Class>[] groups);
/**
* Validate the given return value and return the result of validation.
* @param target the target Object
* @param method the target method
- * @param returnValue value returned from invoking the target method
+ * @param returnType the return parameter, if already created and available
+ * @param returnValue the return value to validate
* @param groups groups for validation determined via
* {@link #determineValidationGroups(Object, Method)}
* @throws MethodValidationException in case of validation errors
*/
- void validateReturnValue(Object target, Method method, @Nullable Object returnValue, Class>[] groups);
+ void validateReturnValue(
+ Object target, Method method, @Nullable MethodParameter returnType, @Nullable Object returnValue,
+ Class>[] groups);
}
diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationAdapterTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationAdapterTests.java
index 239abf0d79..fe98f8cd73 100644
--- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationAdapterTests.java
+++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/MethodValidationAdapterTests.java
@@ -181,14 +181,14 @@ public class MethodValidationAdapterTests {
Object target, Method method, Object[] arguments, Consumer assertions) {
assertions.accept(
- this.validationAdapter.validateMethodArguments(target, method, arguments, new Class>[0]));
+ this.validationAdapter.validateMethodArguments(target, method, null, arguments, new Class>[0]));
}
private void validateReturnValue(
Object target, Method method, @Nullable Object returnValue, Consumer assertions) {
assertions.accept(
- this.validationAdapter.validateMethodReturnValue(target, method, returnValue, new Class>[0]));
+ this.validationAdapter.validateMethodReturnValue(target, method, null, returnValue, new Class>[0]));
}
private static void assertBeanResult(
diff --git a/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodValidator.java b/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodValidator.java
index f04efcb270..862fe44683 100644
--- a/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodValidator.java
+++ b/spring-web/src/main/java/org/springframework/web/method/support/HandlerMethodValidator.java
@@ -46,28 +46,16 @@ import org.springframework.web.method.annotation.ModelFactory;
* @author Rossen Stoyanchev
* @since 6.1
*/
-public class HandlerMethodValidator extends DefaultMethodValidator {
+public final class HandlerMethodValidator extends DefaultMethodValidator {
- public HandlerMethodValidator(MethodValidationAdapter adapter) {
+ private HandlerMethodValidator(MethodValidationAdapter adapter) {
super(adapter);
- adapter.setBindingResultNameResolver(this::determineObjectName);
- }
-
- private String determineObjectName(MethodParameter param, @Nullable Object argument) {
- if (param.hasParameterAnnotation(RequestBody.class) || param.hasParameterAnnotation(RequestPart.class)) {
- return Conventions.getVariableNameForParameter(param);
- }
- else {
- return ((param.getParameterIndex() != -1) ?
- ModelFactory.getNameForParameter(param) :
- ModelFactory.getNameForReturnValue(argument, param));
- }
}
@Override
- protected void handleArgumentsResult(
+ protected void handleArgumentsValidationResult(
Object bean, Method method, Object[] arguments, Class>[] groups, MethodValidationResult result) {
if (result.getConstraintViolations().isEmpty()) {
@@ -93,12 +81,21 @@ public class HandlerMethodValidator extends DefaultMethodValidator {
result.throwIfViolationsPresent();
}
+ private String determineObjectName(MethodParameter param, @Nullable Object argument) {
+ if (param.hasParameterAnnotation(RequestBody.class) || param.hasParameterAnnotation(RequestPart.class)) {
+ return Conventions.getVariableNameForParameter(param);
+ }
+ else {
+ return ((param.getParameterIndex() != -1) ?
+ ModelFactory.getNameForParameter(param) :
+ ModelFactory.getNameForReturnValue(argument, param));
+ }
+ }
+
/**
- * Create a {@link MethodValidator} if Bean Validation is enabled in Spring MVC or WebFlux.
- * @param bindingInitializer for the configured Validator and MessageCodesResolver
- * @param parameterNameDiscoverer the {@code ParameterNameDiscoverer} to use
- * for {@link MethodValidationAdapter#setParameterNameDiscoverer}
+ * Static factory method to create a {@link HandlerMethodValidator} if Bean
+ * Validation is enabled in Spring MVC or WebFlux.
*/
@Nullable
public static MethodValidator from(
@@ -107,15 +104,17 @@ public class HandlerMethodValidator extends DefaultMethodValidator {
if (bindingInitializer instanceof ConfigurableWebBindingInitializer configurableInitializer) {
if (configurableInitializer.getValidator() instanceof Validator validator) {
- MethodValidationAdapter validationAdapter = new MethodValidationAdapter(validator);
+ MethodValidationAdapter adapter = new MethodValidationAdapter(validator);
if (parameterNameDiscoverer != null) {
- validationAdapter.setParameterNameDiscoverer(parameterNameDiscoverer);
+ adapter.setParameterNameDiscoverer(parameterNameDiscoverer);
}
MessageCodesResolver codesResolver = configurableInitializer.getMessageCodesResolver();
if (codesResolver != null) {
- validationAdapter.setMessageCodesResolver(codesResolver);
+ adapter.setMessageCodesResolver(codesResolver);
}
- return new HandlerMethodValidator(validationAdapter);
+ HandlerMethodValidator methodValidator = new HandlerMethodValidator(adapter);
+ adapter.setBindingResultNameResolver(methodValidator::determineObjectName);
+ return methodValidator;
}
}
return null;
diff --git a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java
index f2c38698df..58a7d8a19e 100644
--- a/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java
+++ b/spring-web/src/main/java/org/springframework/web/method/support/InvocableHandlerMethod.java
@@ -168,13 +168,15 @@ public class InvocableHandlerMethod extends HandlerMethod {
Class>[] groups = getValidationGroups();
if (shouldValidateArguments() && this.methodValidator != null) {
- this.methodValidator.validateArguments(getBean(), getBridgedMethod(), args, groups);
+ this.methodValidator.validateArguments(
+ getBean(), getBridgedMethod(), getMethodParameters(), args, groups);
}
Object returnValue = doInvoke(args);
if (shouldValidateReturnValue() && this.methodValidator != null) {
- this.methodValidator.validateReturnValue(getBean(), getBridgedMethod(), returnValue, groups);
+ this.methodValidator.validateReturnValue(
+ getBean(), getBridgedMethod(), getReturnType(), returnValue, groups);
}
return returnValue;
diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java
index 8fd390ef9a..44d564d31c 100644
--- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java
+++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/InvocableHandlerMethod.java
@@ -152,7 +152,8 @@ public class InvocableHandlerMethod extends HandlerMethod {
return getMethodArgumentValues(exchange, bindingContext, providedArgs).flatMap(args -> {
Class>[] groups = getValidationGroups();
if (shouldValidateArguments() && this.methodValidator != null) {
- this.methodValidator.validateArguments(getBean(), getBridgedMethod(), args, groups);
+ this.methodValidator.validateArguments(
+ getBean(), getBridgedMethod(), getMethodParameters(), args, groups);
}
Object value;
Method method = getBridgedMethod();