Skip to content

Commit 9ade52d

Browse files
committed
Exclude Part and MultipartFile from nested constructor binding
Closes gh-31669
1 parent feef98b commit 9ade52d

File tree

4 files changed

+33
-3
lines changed

4 files changed

+33
-3
lines changed

spring-context/src/main/java/org/springframework/validation/DataBinder.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,7 @@ private Object createObject(ResolvableType objectType, String nestedPath, ValueR
948948
Class<?> paramType = paramTypes[i];
949949
Object value = valueResolver.resolveValue(paramPath, paramType);
950950

951-
if (value == null && shouldCreateObject(param)) {
951+
if (value == null && shouldConstructArgument(param)) {
952952
ResolvableType type = ResolvableType.forMethodParameter(param);
953953
args[i] = createObject(type, paramPath + ".", valueResolver);
954954
}
@@ -1008,7 +1008,14 @@ private Object createObject(ResolvableType objectType, String nestedPath, ValueR
10081008
return (isOptional && !nestedPath.isEmpty() ? Optional.ofNullable(result) : result);
10091009
}
10101010

1011-
private static boolean shouldCreateObject(MethodParameter param) {
1011+
/**
1012+
* Whether to instantiate the constructor argument of the given type,
1013+
* matching its own constructor arguments to bind values.
1014+
* <p>By default, simple value types, maps, collections, and arrays are
1015+
* excluded from nested constructor binding initialization.
1016+
* @since 6.1.2
1017+
*/
1018+
protected boolean shouldConstructArgument(MethodParameter param) {
10121019
Class<?> type = param.nestedIfOptional().getNestedParameterType();
10131020
return !(BeanUtils.isSimpleValueType(type) ||
10141021
Collection.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type) || type.isArray());

spring-web/src/main/java/org/springframework/web/bind/ServletRequestDataBinder.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import jakarta.servlet.http.Part;
2525

2626
import org.springframework.beans.MutablePropertyValues;
27+
import org.springframework.core.MethodParameter;
2728
import org.springframework.http.HttpMethod;
2829
import org.springframework.http.MediaType;
2930
import org.springframework.lang.Nullable;
@@ -120,6 +121,13 @@ protected ServletRequestValueResolver createValueResolver(ServletRequest request
120121
return new ServletRequestValueResolver(request, this);
121122
}
122123

124+
@Override
125+
protected boolean shouldConstructArgument(MethodParameter param) {
126+
Class<?> type = param.nestedIfOptional().getNestedParameterType();
127+
return (super.shouldConstructArgument(param) &&
128+
!MultipartFile.class.isAssignableFrom(type) && !Part.class.isAssignableFrom(type));
129+
}
130+
123131
/**
124132
* Bind the parameters of the given request to this binder's target,
125133
* also binding multipart files in case of a multipart request.

spring-web/src/main/java/org/springframework/web/bind/support/WebRequestDataBinder.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818

1919
import jakarta.servlet.ServletRequest;
2020
import jakarta.servlet.http.HttpServletRequest;
21+
import jakarta.servlet.http.Part;
2122

2223
import org.springframework.beans.MutablePropertyValues;
24+
import org.springframework.core.MethodParameter;
2325
import org.springframework.http.HttpHeaders;
2426
import org.springframework.http.HttpMethod;
2527
import org.springframework.http.MediaType;
@@ -30,6 +32,7 @@
3032
import org.springframework.web.bind.WebDataBinder;
3133
import org.springframework.web.context.request.NativeWebRequest;
3234
import org.springframework.web.context.request.WebRequest;
35+
import org.springframework.web.multipart.MultipartFile;
3336
import org.springframework.web.multipart.MultipartRequest;
3437
import org.springframework.web.multipart.support.StandardServletPartUtils;
3538

@@ -118,6 +121,12 @@ public void construct(WebRequest request) {
118121
}
119122
}
120123

124+
@Override
125+
protected boolean shouldConstructArgument(MethodParameter param) {
126+
Class<?> type = param.nestedIfOptional().getNestedParameterType();
127+
return (super.shouldConstructArgument(param) &&
128+
!MultipartFile.class.isAssignableFrom(type) && !Part.class.isAssignableFrom(type));
129+
}
121130

122131
/**
123132
* Bind the parameters of the given request to this binder's target,

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.core.ResolvableType;
3030
import org.springframework.core.annotation.SynthesizingMethodParameter;
3131
import org.springframework.format.support.DefaultFormattingConversionService;
32+
import org.springframework.lang.Nullable;
3233
import org.springframework.validation.BindingResult;
3334
import org.springframework.validation.Errors;
3435
import org.springframework.web.bind.MethodArgumentNotValidException;
@@ -41,6 +42,7 @@
4142
import org.springframework.web.context.request.ServletWebRequest;
4243
import org.springframework.web.context.request.WebRequest;
4344
import org.springframework.web.method.support.ModelAndViewContainer;
45+
import org.springframework.web.multipart.MultipartFile;
4446
import org.springframework.web.testfixture.servlet.MockHttpServletRequest;
4547

4648
import static java.lang.annotation.ElementType.CONSTRUCTOR;
@@ -298,6 +300,7 @@ public void resolveConstructorListArgumentFromCommaSeparatedRequestParameter() t
298300
Object resolved = this.processor.resolveArgument(this.beanWithConstructorArgs, this.container, requestWithParam, factory);
299301
assertThat(resolved).isInstanceOf(TestBeanWithConstructorArgs.class);
300302
assertThat(((TestBeanWithConstructorArgs) resolved).listOfStrings).containsExactly("1", "2");
303+
assertThat(((TestBeanWithConstructorArgs) resolved).file).isNull();
301304
}
302305

303306
private void testGetAttributeFromModel(String expectedAttrName, MethodParameter param) throws Exception {
@@ -375,8 +378,11 @@ static class TestBeanWithConstructorArgs {
375378

376379
final List<String> listOfStrings;
377380

378-
public TestBeanWithConstructorArgs(List<String> listOfStrings) {
381+
final MultipartFile file;
382+
383+
public TestBeanWithConstructorArgs(List<String> listOfStrings, @Nullable MultipartFile file) {
379384
this.listOfStrings = listOfStrings;
385+
this.file = file;
380386
}
381387
}
382388

0 commit comments

Comments
 (0)