Skip to content

Commit 11a4161

Browse files
committed
Use ResolvableType to create WebDataBinder
This provides more flexibility to pass a targetType even if a MethodParameter is not available. See gh-26721
1 parent d37d668 commit 11a4161

File tree

10 files changed

+56
-42
lines changed

10 files changed

+56
-42
lines changed

Diff for: spring-web/src/main/java/org/springframework/web/bind/support/DefaultDataBinderFactory.java

+9-7
Original file line numberDiff line numberDiff line change
@@ -81,28 +81,30 @@ public final WebDataBinder createBinder(
8181
@Override
8282
public final WebDataBinder createBinder(
8383
NativeWebRequest webRequest, @Nullable Object target, String objectName,
84-
MethodParameter parameter) throws Exception {
84+
ResolvableType type) throws Exception {
8585

86-
return createBinderInternal(webRequest, target, objectName, parameter);
86+
return createBinderInternal(webRequest, target, objectName, type);
8787
}
8888

8989
private WebDataBinder createBinderInternal(
9090
NativeWebRequest webRequest, @Nullable Object target, String objectName,
91-
@Nullable MethodParameter parameter) throws Exception {
91+
@Nullable ResolvableType type) throws Exception {
9292

9393
WebDataBinder dataBinder = createBinderInstance(target, objectName, webRequest);
9494

95-
if (target == null && parameter != null) {
96-
dataBinder.setTargetType(ResolvableType.forMethodParameter(parameter));
95+
if (target == null && type != null) {
96+
dataBinder.setTargetType(type);
9797
}
9898

9999
if (this.initializer != null) {
100100
this.initializer.initBinder(dataBinder);
101101
}
102102
initBinder(dataBinder, webRequest);
103103

104-
if (this.methodValidationApplicable && parameter != null) {
105-
MethodValidationInitializer.initBinder(dataBinder, parameter);
104+
if (this.methodValidationApplicable && type != null) {
105+
if (type.getSource() instanceof MethodParameter parameter) {
106+
MethodValidationInitializer.initBinder(dataBinder, parameter);
107+
}
106108
}
107109

108110
return dataBinder;

Diff for: spring-web/src/main/java/org/springframework/web/bind/support/WebDataBinderFactory.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package org.springframework.web.bind.support;
1818

19-
import org.springframework.core.MethodParameter;
19+
import org.springframework.core.ResolvableType;
2020
import org.springframework.lang.Nullable;
2121
import org.springframework.web.bind.WebDataBinder;
2222
import org.springframework.web.context.request.NativeWebRequest;
@@ -44,13 +44,14 @@ WebDataBinder createBinder(NativeWebRequest webRequest, @Nullable Object target,
4444

4545
/**
4646
* Variant of {@link #createBinder(NativeWebRequest, Object, String)} with a
47-
* {@link MethodParameter} for which the {@code DataBinder} is created. This
48-
* may provide more insight to initialize the {@link WebDataBinder}.
47+
* {@link ResolvableType} for which the {@code DataBinder} is created.
48+
* This may be used to construct the target, or otherwise provide more
49+
* insight on how to initialize the binder.
4950
* @since 6.1
5051
*/
5152
default WebDataBinder createBinder(
5253
NativeWebRequest webRequest, @Nullable Object target, String objectName,
53-
MethodParameter parameter) throws Exception {
54+
ResolvableType targetType) throws Exception {
5455

5556
return createBinder(webRequest, target, objectName);
5657
}

Diff for: spring-web/src/main/java/org/springframework/web/bind/support/WebExchangeDataBinder.java

+2-8
Original file line numberDiff line numberDiff line change
@@ -148,18 +148,12 @@ protected static void addBindValue(Map<String, Object> params, String key, List<
148148
/**
149149
* Resolve values from a map.
150150
*/
151-
private static class MapValueResolver implements ValueResolver {
152-
153-
private final Map<String, Object> map;
154-
155-
private MapValueResolver(Map<String, Object> map) {
156-
this.map = map;
157-
}
151+
private record MapValueResolver(Map<String, Object> map) implements ValueResolver {
158152

159153
@Override
160154
public Object resolveValue(String name, Class<?> type) {
161155
return this.map.get(name);
162156
}
163157
}
164158

165-
}
159+
}

Diff for: spring-web/src/main/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessor.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.springframework.beans.BeanUtils;
2828
import org.springframework.core.MethodParameter;
29+
import org.springframework.core.ResolvableType;
2930
import org.springframework.lang.Nullable;
3031
import org.springframework.util.Assert;
3132
import org.springframework.util.ObjectUtils;
@@ -142,7 +143,8 @@ public final Object resolveArgument(MethodParameter parameter, @Nullable ModelAn
142143

143144
// No BindingResult yet, proceed with binding and validation
144145
if (bindingResult == null) {
145-
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name, parameter);
146+
ResolvableType type = ResolvableType.forMethodParameter(parameter);
147+
WebDataBinder binder = binderFactory.createBinder(webRequest, attribute, name, type);
146148
if (attribute == null) {
147149
constructAttribute(binder, webRequest);
148150
attribute = wrapAsOptionalIfNecessary(parameter, binder.getTarget());

Diff for: spring-web/src/test/java/org/springframework/web/method/annotation/ModelAttributeMethodProcessorTests.java

+14-8
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,8 @@ public void resolveArgumentValidation() throws Exception {
179179

180180
StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name);
181181
WebDataBinderFactory factory = mock();
182-
given(factory.createBinder(this.request, target, name, this.paramNamedValidModelAttr)).willReturn(dataBinder);
182+
ResolvableType type = ResolvableType.forMethodParameter(this.paramNamedValidModelAttr);
183+
given(factory.createBinder(this.request, target, name, type)).willReturn(dataBinder);
183184

184185
this.processor.resolveArgument(this.paramNamedValidModelAttr, this.container, this.request, factory);
185186

@@ -198,7 +199,8 @@ public void resolveArgumentBindingDisabledPreviously() throws Exception {
198199

199200
StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name);
200201
WebDataBinderFactory factory = mock();
201-
given(factory.createBinder(this.request, target, name, this.paramNamedValidModelAttr)).willReturn(dataBinder);
202+
ResolvableType type = ResolvableType.forMethodParameter(this.paramNamedValidModelAttr);
203+
given(factory.createBinder(this.request, target, name, type)).willReturn(dataBinder);
202204

203205
this.processor.resolveArgument(this.paramNamedValidModelAttr, this.container, this.request, factory);
204206

@@ -214,7 +216,8 @@ public void resolveArgumentBindingDisabled() throws Exception {
214216

215217
StubRequestDataBinder dataBinder = new StubRequestDataBinder(target, name);
216218
WebDataBinderFactory factory = mock();
217-
given(factory.createBinder(this.request, target, name, this.paramBindingDisabledAttr)).willReturn(dataBinder);
219+
ResolvableType type = ResolvableType.forMethodParameter(this.paramBindingDisabledAttr);
220+
given(factory.createBinder(this.request, target, name, type)).willReturn(dataBinder);
218221

219222
this.processor.resolveArgument(this.paramBindingDisabledAttr, this.container, this.request, factory);
220223

@@ -232,12 +235,13 @@ public void resolveArgumentBindException() throws Exception {
232235
dataBinder.getBindingResult().reject("error");
233236

234237
WebDataBinderFactory binderFactory = mock();
235-
given(binderFactory.createBinder(this.request, target, name, this.paramNonSimpleType)).willReturn(dataBinder);
238+
ResolvableType type = ResolvableType.forMethodParameter(this.paramNonSimpleType);
239+
given(binderFactory.createBinder(this.request, target, name, type)).willReturn(dataBinder);
236240

237241
assertThatExceptionOfType(MethodArgumentNotValidException.class).isThrownBy(() ->
238242
this.processor.resolveArgument(this.paramNonSimpleType, this.container, this.request, binderFactory));
239243

240-
verify(binderFactory).createBinder(this.request, target, name, this.paramNonSimpleType);
244+
verify(binderFactory).createBinder(this.request, target, name, type);
241245
}
242246

243247
@Test // SPR-9378
@@ -252,7 +256,8 @@ public void resolveArgumentOrdering() throws Exception {
252256

253257
StubRequestDataBinder dataBinder = new StubRequestDataBinder(testBean, name);
254258
WebDataBinderFactory binderFactory = mock();
255-
given(binderFactory.createBinder(this.request, testBean, name, this.paramModelAttr)).willReturn(dataBinder);
259+
ResolvableType type = ResolvableType.forMethodParameter(this.paramModelAttr);
260+
given(binderFactory.createBinder(this.request, testBean, name, type)).willReturn(dataBinder);
256261

257262
this.processor.resolveArgument(this.paramModelAttr, this.container, this.request, binderFactory);
258263

@@ -301,10 +306,11 @@ private void testGetAttributeFromModel(String expectedAttrName, MethodParameter
301306

302307
WebDataBinder dataBinder = new WebRequestDataBinder(target);
303308
WebDataBinderFactory factory = mock();
304-
given(factory.createBinder(this.request, target, expectedAttrName, param)).willReturn(dataBinder);
309+
ResolvableType type = ResolvableType.forMethodParameter(param);
310+
given(factory.createBinder(this.request, target, expectedAttrName, type)).willReturn(dataBinder);
305311

306312
this.processor.resolveArgument(param, this.container, this.request, factory);
307-
verify(factory).createBinder(this.request, target, expectedAttrName, param);
313+
verify(factory).createBinder(this.request, target, expectedAttrName, type);
308314
}
309315

310316

Diff for: spring-webflux/src/main/java/org/springframework/web/reactive/BindingContext.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -116,26 +116,28 @@ public WebExchangeDataBinder createDataBinder(ServerWebExchange exchange, String
116116
}
117117

118118
/**
119-
* Create a binder with a target object and a {@code MethodParameter}.
119+
* Create a binder with a target object and a {@link ResolvableType targetType}.
120120
* If the target is {@code null}, then
121121
* {@link WebExchangeDataBinder#setTargetType targetType} is set.
122122
* @since 6.1
123123
*/
124124
public WebExchangeDataBinder createDataBinder(
125-
ServerWebExchange exchange, @Nullable Object target, String name, @Nullable MethodParameter parameter) {
125+
ServerWebExchange exchange, @Nullable Object target, String name, @Nullable ResolvableType targetType) {
126126

127127
WebExchangeDataBinder dataBinder = new ExtendedWebExchangeDataBinder(target, name);
128-
if (target == null && parameter != null) {
129-
dataBinder.setTargetType(ResolvableType.forMethodParameter(parameter));
128+
if (target == null && targetType != null) {
129+
dataBinder.setTargetType(targetType);
130130
}
131131

132132
if (this.initializer != null) {
133133
this.initializer.initBinder(dataBinder);
134134
}
135135
dataBinder = initDataBinder(dataBinder, exchange);
136136

137-
if (this.methodValidationApplicable && parameter != null) {
138-
MethodValidationInitializer.initBinder(dataBinder, parameter);
137+
if (this.methodValidationApplicable && targetType != null) {
138+
if (targetType.getSource() instanceof MethodParameter parameter) {
139+
MethodValidationInitializer.initBinder(dataBinder, parameter);
140+
}
139141
}
140142

141143
return dataBinder;

Diff for: spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/AbstractMessageReaderArgumentResolver.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -265,11 +265,12 @@ private Object[] extractValidationHints(MethodParameter parameter) {
265265
return null;
266266
}
267267

268-
private void validate(Object target, Object[] validationHints, MethodParameter param,
268+
private void validate(Object target, Object[] validationHints, MethodParameter parameter,
269269
BindingContext binding, ServerWebExchange exchange) {
270270

271-
String name = Conventions.getVariableNameForParameter(param);
272-
WebExchangeDataBinder binder = binding.createDataBinder(exchange, target, name, param);
271+
String name = Conventions.getVariableNameForParameter(parameter);
272+
ResolvableType type = ResolvableType.forMethodParameter(parameter);
273+
WebExchangeDataBinder binder = binding.createDataBinder(exchange, target, name, type);
273274
try {
274275
LocaleContextHolder.setLocaleContext(exchange.getLocaleContext());
275276
binder.validate(validationHints);
@@ -278,7 +279,7 @@ private void validate(Object target, Object[] validationHints, MethodParameter p
278279
LocaleContextHolder.resetLocaleContext();
279280
}
280281
if (binder.getBindingResult().hasErrors()) {
281-
throw new WebExchangeBindException(param, binder.getBindingResult());
282+
throw new WebExchangeBindException(parameter, binder.getBindingResult());
282283
}
283284
}
284285

Diff for: spring-webflux/src/main/java/org/springframework/web/reactive/result/method/annotation/ModelAttributeMethodArgumentResolver.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import org.springframework.core.MethodParameter;
2929
import org.springframework.core.ReactiveAdapter;
3030
import org.springframework.core.ReactiveAdapterRegistry;
31+
import org.springframework.core.ResolvableType;
3132
import org.springframework.lang.Nullable;
3233
import org.springframework.ui.Model;
3334
import org.springframework.util.Assert;
@@ -150,14 +151,15 @@ private Mono<WebExchangeDataBinder> initDataBinder(
150151
if (value == null) {
151152
value = removeReactiveAttribute(name, context.getModel());
152153
}
154+
ResolvableType type = ResolvableType.forMethodParameter(parameter);
153155
if (value != null) {
154156
ReactiveAdapter adapter = getAdapterRegistry().getAdapter(null, value);
155157
Assert.isTrue(adapter == null || !adapter.isMultiValue(), "Multi-value publisher is not supported");
156158
return (adapter != null ? Mono.from(adapter.toPublisher(value)) : Mono.just(value))
157-
.map(attr -> context.createDataBinder(exchange, attr, name, parameter));
159+
.map(attr -> context.createDataBinder(exchange, attr, name, type));
158160
}
159161
else {
160-
WebExchangeDataBinder binder = context.createDataBinder(exchange, null, name, parameter);
162+
WebExchangeDataBinder binder = context.createDataBinder(exchange, null, name, type);
161163
return constructAttribute(binder, exchange).thenReturn(binder);
162164
}
163165
}

Diff for: spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestPartMethodArgumentResolver.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import jakarta.servlet.http.HttpServletRequest;
2424

2525
import org.springframework.core.MethodParameter;
26+
import org.springframework.core.ResolvableType;
2627
import org.springframework.http.HttpInputMessage;
2728
import org.springframework.http.converter.HttpMessageConverter;
2829
import org.springframework.lang.Nullable;
@@ -139,7 +140,8 @@ public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewC
139140
HttpInputMessage inputMessage = new RequestPartServletServerHttpRequest(servletRequest, name);
140141
arg = readWithMessageConverters(inputMessage, parameter, parameter.getNestedGenericParameterType());
141142
if (binderFactory != null) {
142-
WebDataBinder binder = binderFactory.createBinder(request, arg, name, parameter);
143+
ResolvableType type = ResolvableType.forMethodParameter(parameter);
144+
WebDataBinder binder = binderFactory.createBinder(request, arg, name, type);
143145
if (arg != null) {
144146
validateIfApplicable(binder, parameter);
145147
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {

Diff for: spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestResponseBodyMethodProcessor.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import org.springframework.core.Conventions;
2525
import org.springframework.core.MethodParameter;
26+
import org.springframework.core.ResolvableType;
2627
import org.springframework.core.annotation.AnnotatedElementUtils;
2728
import org.springframework.http.HttpStatusCode;
2829
import org.springframework.http.ProblemDetail;
@@ -134,7 +135,8 @@ public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewC
134135
String name = Conventions.getVariableNameForParameter(parameter);
135136

136137
if (binderFactory != null) {
137-
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name, parameter);
138+
ResolvableType type = ResolvableType.forMethodParameter(parameter);
139+
WebDataBinder binder = binderFactory.createBinder(webRequest, arg, name, type);
138140
if (arg != null) {
139141
validateIfApplicable(binder, parameter);
140142
if (binder.getBindingResult().hasErrors() && isBindExceptionRequired(binder, parameter)) {

0 commit comments

Comments
 (0)