diff --git a/spring-web/src/main/java/org/springframework/web/bind/support/DefaultDataBinderFactory.java b/spring-web/src/main/java/org/springframework/web/bind/support/DefaultDataBinderFactory.java index f65b875fbb..372b714af5 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/support/DefaultDataBinderFactory.java +++ b/spring-web/src/main/java/org/springframework/web/bind/support/DefaultDataBinderFactory.java @@ -81,19 +81,19 @@ public class DefaultDataBinderFactory implements WebDataBinderFactory { @Override public final WebDataBinder createBinder( NativeWebRequest webRequest, @Nullable Object target, String objectName, - MethodParameter parameter) throws Exception { + ResolvableType type) throws Exception { - return createBinderInternal(webRequest, target, objectName, parameter); + return createBinderInternal(webRequest, target, objectName, type); } private WebDataBinder createBinderInternal( NativeWebRequest webRequest, @Nullable Object target, String objectName, - @Nullable MethodParameter parameter) throws Exception { + @Nullable ResolvableType type) throws Exception { WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest); - if (target == null && parameter != null) { - dataBinder.setTargetType(ResolvableType.forMethodParameter(parameter)); + if (target == null && type != null) { + dataBinder.setTargetType(type); } if (this.initializer != null) { @@ -101,8 +101,10 @@ public class DefaultDataBinderFactory implements WebDataBinderFactory { } initBinder(dataBinder, webRequest); - if (this.methodValidationApplicable && parameter != null) { - MethodValidationInitializer.initBinder(dataBinder, parameter); + if (this.methodValidationApplicable && type != null) { + if (type.getSource() instanceof MethodParameter parameter) { + MethodValidationInitializer.initBinder(dataBinder, parameter); + } } return dataBinder; diff --git a/spring-web/src/main/java/org/springframework/web/bind/support/WebDataBinderFactory.java b/spring-web/src/main/java/org/springframework/web/bind/support/WebDataBinderFactory.java index bf1981a99b..3411d363e2 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/support/WebDataBinderFactory.java +++ b/spring-web/src/main/java/org/springframework/web/bind/support/WebDataBinderFactory.java @@ -16,7 +16,7 @@ package org.springframework.web.bind.support; -import org.springframework.core.MethodParameter; +import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.context.request.NativeWebRequest; @@ -44,13 +44,14 @@ public interface WebDataBinderFactory { /** * Variant of {@link #createBinder(NativeWebRequest, Object, String)} with a - * {@link MethodParameter} for which the {@code DataBinder} is created. This - * may provide more insight to initialize the {@link WebDataBinder}. + * {@link ResolvableType} for which the {@code DataBinder} is created. + * This may be used to construct the target, or otherwise provide more + * insight on how to initialize the binder. * @since 6.1 */ default WebDataBinder createBinder( NativeWebRequest webRequest, @Nullable Object target, String objectName, - MethodParameter parameter) throws Exception { + ResolvableType targetType) throws Exception { return createBinder(webRequest, target, objectName); } diff --git a/spring-web/src/main/java/org/springframework/web/bind/support/WebExchangeDataBinder.java b/spring-web/src/main/java/org/springframework/web/bind/support/WebExchangeDataBinder.java index 8abcd8e286..6367227dfa 100644 --- a/spring-web/src/main/java/org/springframework/web/bind/support/WebExchangeDataBinder.java +++ b/spring-web/src/main/java/org/springframework/web/bind/support/WebExchangeDataBinder.java @@ -148,13 +148,7 @@ public class WebExchangeDataBinder extends WebDataBinder { /** * Resolve values from a map. */ - private static class MapValueResolver implements ValueResolver { - - private final Map map; - - private MapValueResolver(Map map) { - this.map = map; - } + private record MapValueResolver(Map map) implements ValueResolver { @Override public Object resolveValue(String name, Class type) { @@ -162,4 +156,4 @@ public class WebExchangeDataBinder extends WebDataBinder { } } -} \ No newline at end of file +} diff --git a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java index 929fc6b0fa..8faec818d2 100644 --- a/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java +++ b/spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java @@ -26,6 +26,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeanUtils; import org.springframework.core.MethodParameter; +import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; @@ -142,7 +143,8 @@ public class ModelAttributeMethodProcessor implements HandlerMethodArgumentResol // No BindingResult yet, proceed with binding and validation if (bindingResult == null) { - WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name, parameter); + ResolvableType type = ResolvableType.forMethodParameter(parameter); + WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name, type); if (attribute == null) { constructAttribute(binder, webRequest); attribute = wrapAsOptionalIfNecessary(parameter, binder.getTarget()); diff --git a/spring-web/src/test/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessorTests.java b/spring-web/src/test/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessorTests.java index d926dd25d2..826d768463 100644 --- a/spring-web/src/test/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessorTests.java +++ b/spring-web/src/test/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessorTests.java @@ -179,7 +179,8 @@ public class ModelAttributeMethodProcessorTests { StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name); WebDataBinderFactory factory = mock(); - given(factory.createBinder(this.request, target, name, this.paramNamedValidModelAttr)).willReturn(dataBinder); + ResolvableType type = ResolvableType.forMethodParameter(this.paramNamedValidModelAttr); + given(factory.createBinder(this.request, target, name, type)).willReturn(dataBinder); this.processor.resolveArgument(this.paramNamedValidModelAttr, this.container, this.request, factory); @@ -198,7 +199,8 @@ public class ModelAttributeMethodProcessorTests { StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name); WebDataBinderFactory factory = mock(); - given(factory.createBinder(this.request, target, name, this.paramNamedValidModelAttr)).willReturn(dataBinder); + ResolvableType type = ResolvableType.forMethodParameter(this.paramNamedValidModelAttr); + given(factory.createBinder(this.request, target, name, type)).willReturn(dataBinder); this.processor.resolveArgument(this.paramNamedValidModelAttr, this.container, this.request, factory); @@ -214,7 +216,8 @@ public class ModelAttributeMethodProcessorTests { StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name); WebDataBinderFactory factory = mock(); - given(factory.createBinder(this.request, target, name, this.paramBindingDisabledAttr)).willReturn(dataBinder); + ResolvableType type = ResolvableType.forMethodParameter(this.paramBindingDisabledAttr); + given(factory.createBinder(this.request, target, name, type)).willReturn(dataBinder); this.processor.resolveArgument(this.paramBindingDisabledAttr, this.container, this.request, factory); @@ -232,12 +235,13 @@ public class ModelAttributeMethodProcessorTests { dataBinder.getBindingResult().reject("error"); WebDataBinderFactory binderFactory = mock(); - given(binderFactory.createBinder(this.request, target, name, this.paramNonSimpleType)).willReturn(dataBinder); + ResolvableType type = ResolvableType.forMethodParameter(this.paramNonSimpleType); + given(binderFactory.createBinder(this.request, target, name, type)).willReturn(dataBinder); assertThatExceptionOfType(MethodArgumentNotValidException.class).isThrownBy(() -> this.processor.resolveArgument(this.paramNonSimpleType, this.container, this.request, binderFactory)); - verify(binderFactory).createBinder(this.request, target, name, this.paramNonSimpleType); + verify(binderFactory).createBinder(this.request, target, name, type); } @Test // SPR-9378 @@ -252,7 +256,8 @@ public class ModelAttributeMethodProcessorTests { StubRequestDataBinder dataBinder = new StubRequestDataBinder(testBean, name); WebDataBinderFactory binderFactory = mock(); - given(binderFactory.createBinder(this.request, testBean, name, this.paramModelAttr)).willReturn(dataBinder); + ResolvableType type = ResolvableType.forMethodParameter(this.paramModelAttr); + given(binderFactory.createBinder(this.request, testBean, name, type)).willReturn(dataBinder); this.processor.resolveArgument(this.paramModelAttr, this.container, this.request, binderFactory); @@ -301,10 +306,11 @@ public class ModelAttributeMethodProcessorTests { WebDataBinder dataBinder = new WebRequestDataBinder(target); WebDataBinderFactory factory = mock(); - given(factory.createBinder(this.request, target, expectedAttrName, param)).willReturn(dataBinder); + ResolvableType type = ResolvableType.forMethodParameter(param); + given(factory.createBinder(this.request, target, expectedAttrName, type)).willReturn(dataBinder); this.processor.resolveArgument(param, this.container, this.request, factory); - verify(factory).createBinder(this.request, target, expectedAttrName, param); + verify(factory).createBinder(this.request, target, expectedAttrName, type); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/BindingContext.java b/spring-webflux/src/main/java/org/springframework/web/reactive/BindingContext.java index f336047019..803bb7d905 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/BindingContext.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/BindingContext.java @@ -116,17 +116,17 @@ public class BindingContext { } /** - * Create a binder with a target object and a {@code MethodParameter}. + * Create a binder with a target object and a {@link ResolvableType targetType}. * If the target is {@code null}, then * {@link WebExchangeDataBinder#setTargetType targetType} is set. * @since 6.1 */ public WebExchangeDataBinder createDataBinder( - ServerWebExchange exchange, @Nullable Object target, String name, @Nullable MethodParameter parameter) { + ServerWebExchange exchange, @Nullable Object target, String name, @Nullable ResolvableType targetType) { WebExchangeDataBinder dataBinder = new ExtendedWebExchangeDataBinder(target, name); - if (target == null && parameter != null) { - dataBinder.setTargetType(ResolvableType.forMethodParameter(parameter)); + if (target == null && targetType != null) { + dataBinder.setTargetType(targetType); } if (this.initializer != null) { @@ -134,8 +134,10 @@ public class BindingContext { } dataBinder = initDataBinder(dataBinder, exchange); - if (this.methodValidationApplicable && parameter != null) { - MethodValidationInitializer.initBinder(dataBinder, parameter); + if (this.methodValidationApplicable && targetType != null) { + if (targetType.getSource() instanceof MethodParameter parameter) { + MethodValidationInitializer.initBinder(dataBinder, parameter); + } } return dataBinder; diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java index cbb8db1ef2..db735c3e87 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java @@ -265,11 +265,12 @@ public abstract class AbstractMessageReaderArgumentResolver extends HandlerMetho return null; } - private void validate(Object target, Object[] validationHints, MethodParameter param, + private void validate(Object target, Object[] validationHints, MethodParameter parameter, BindingContext binding, ServerWebExchange exchange) { - String name = Conventions.getVariableNameForParameter(param); - WebExchangeDataBinder binder = binding.createDataBinder(exchange, target, name, param); + String name = Conventions.getVariableNameForParameter(parameter); + ResolvableType type = ResolvableType.forMethodParameter(parameter); + WebExchangeDataBinder binder = binding.createDataBinder(exchange, target, name, type); try { LocaleContextHolder.setLocaleContext(exchange.getLocaleContext()); binder.validate(validationHints); @@ -278,7 +279,7 @@ public abstract class AbstractMessageReaderArgumentResolver extends HandlerMetho LocaleContextHolder.resetLocaleContext(); } if (binder.getBindingResult().hasErrors()) { - throw new WebExchangeBindException(param, binder.getBindingResult()); + throw new WebExchangeBindException(parameter, binder.getBindingResult()); } } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java index d9987cc9f8..a68f7fbae9 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java @@ -28,6 +28,7 @@ import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.core.MethodParameter; import org.springframework.core.ReactiveAdapter; import org.springframework.core.ReactiveAdapterRegistry; +import org.springframework.core.ResolvableType; import org.springframework.lang.Nullable; import org.springframework.ui.Model; import org.springframework.util.Assert; @@ -150,14 +151,15 @@ public class ModelAttributeMethodArgumentResolver extends HandlerMethodArgumentR if (value == null) { value = removeReactiveAttribute(name, context.getModel()); } + ResolvableType type = ResolvableType.forMethodParameter(parameter); if (value != null) { ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, value); Assert.isTrue(adapter == null || !adapter.isMultiValue(), "Multi-value publisher is not supported"); return (adapter != null ? Mono.from(adapter.toPublisher(value)) : Mono.just(value)) - .map(attr -> context.createDataBinder(exchange, attr, name, parameter)); + .map(attr -> context.createDataBinder(exchange, attr, name, type)); } else { - WebExchangeDataBinder binder = context.createDataBinder(exchange, null, name, parameter); + WebExchangeDataBinder binder = context.createDataBinder(exchange, null, name, type); return constructAttribute(binder, exchange).thenReturn(binder); } } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java index 0059343764..47e21fcab2 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java @@ -23,6 +23,7 @@ import java.util.List; import jakarta.servlet.http.HttpServletRequest; import org.springframework.core.MethodParameter; +import org.springframework.core.ResolvableType; import org.springframework.http.HttpInputMessage; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.lang.Nullable; @@ -139,7 +140,8 @@ public class RequestPartMethodArgumentResolver extends AbstractMessageConverterM HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, name); arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType()); if (binderFactory != null) { - WebDataBinder binder = binderFactory.createBinder(request, arg, name, parameter); + ResolvableType type = ResolvableType.forMethodParameter(parameter); + WebDataBinder binder = binderFactory.createBinder(request, arg, name, type); if (arg != null) { validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java index 503d667f51..8419bd21c0 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java @@ -23,6 +23,7 @@ import java.util.List; import org.springframework.core.Conventions; import org.springframework.core.MethodParameter; +import org.springframework.core.ResolvableType; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.http.HttpStatusCode; import org.springframework.http.ProblemDetail; @@ -134,7 +135,8 @@ public class RequestResponseBodyMethodProcessor extends AbstractMessageConverter String name = Conventions.getVariableNameForParameter(parameter); if (binderFactory != null) { - WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name, parameter); + ResolvableType type = ResolvableType.forMethodParameter(parameter); + WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name, type); if (arg != null) { validateIfApplicable(binder, parameter); if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {