@@ -170,6 +170,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
170
170
@ Nullable
171
171
private String [] requiredFields ;
172
172
173
+ @ Nullable
174
+ private NameResolver nameResolver ;
175
+
173
176
@ Nullable
174
177
private ConversionService conversionService ;
175
178
@@ -225,7 +228,7 @@ public String getObjectName() {
225
228
226
229
/**
227
230
* Set the type for the target object. When the target is {@code null},
228
- * setting the targetType allows using {@link #construct(ValueResolver) } to
231
+ * setting the targetType allows using {@link #construct} to
229
232
* create the target.
230
233
* @param targetType the type of the target object
231
234
* @since 6.1
@@ -252,7 +255,7 @@ public ResolvableType getTargetType() {
252
255
* <p>Default is "true" on a standard DataBinder. Note that since Spring 4.1 this feature is supported
253
256
* for bean property access (DataBinder's default mode) and field access.
254
257
* <p>Used for setter/field injection via {@link #bind(PropertyValues)}, and not
255
- * applicable to constructor initialization via {@link #construct(ValueResolver) }.
258
+ * applicable to constructor binding via {@link #construct}.
256
259
* @see #initBeanPropertyAccess()
257
260
* @see org.springframework.beans.BeanWrapper#setAutoGrowNestedPaths
258
261
*/
@@ -274,7 +277,7 @@ public boolean isAutoGrowNestedPaths() {
274
277
* <p>Default is 256, preventing OutOfMemoryErrors in case of large indexes.
275
278
* Raise this limit if your auto-growing needs are unusually high.
276
279
* <p>Used for setter/field injection via {@link #bind(PropertyValues)}, and not
277
- * applicable to constructor initialization via {@link #construct(ValueResolver) }.
280
+ * applicable to constructor binding via {@link #construct}.
278
281
* @see #initBeanPropertyAccess()
279
282
* @see org.springframework.beans.BeanWrapper#setAutoGrowCollectionLimit
280
283
*/
@@ -431,8 +434,8 @@ public BindingResult getBindingResult() {
431
434
* <p>Note that this setting only applies to <i>binding</i> operations
432
435
* on this DataBinder, not to <i>retrieving</i> values via its
433
436
* {@link #getBindingResult() BindingResult}.
434
- * <p>Used for setter/field inject via {@link #bind(PropertyValues)}, and not
435
- * applicable to constructor initialization via {@link #construct(ValueResolver) },
437
+ * <p>Used for binding to fields with {@link #bind(PropertyValues)}, and not
438
+ * applicable to constructor binding via {@link #construct},
436
439
* which uses only the values it needs.
437
440
* @see #bind
438
441
*/
@@ -456,8 +459,8 @@ public boolean isIgnoreUnknownFields() {
456
459
* <p>Note that this setting only applies to <i>binding</i> operations
457
460
* on this DataBinder, not to <i>retrieving</i> values via its
458
461
* {@link #getBindingResult() BindingResult}.
459
- * <p>Used for setter/field inject via {@link #bind(PropertyValues)}, and not
460
- * applicable to constructor initialization via {@link #construct(ValueResolver) },
462
+ * <p>Used for binding to fields with {@link #bind(PropertyValues)}, and not
463
+ * applicable to constructor binding via {@link #construct},
461
464
* which uses only the values it needs.
462
465
* @see #bind
463
466
*/
@@ -487,8 +490,8 @@ public boolean isIgnoreInvalidFields() {
487
490
* <p>More sophisticated matching can be implemented by overriding the
488
491
* {@link #isAllowed} method.
489
492
* <p>Alternatively, specify a list of <i>disallowed</i> field patterns.
490
- * <p>Used for setter/field inject via {@link #bind(PropertyValues)}, and not
491
- * applicable to constructor initialization via {@link #construct(ValueResolver) },
493
+ * <p>Used for binding to fields with {@link #bind(PropertyValues)}, and not
494
+ * applicable to constructor binding via {@link #construct},
492
495
* which uses only the values it needs.
493
496
* @param allowedFields array of allowed field patterns
494
497
* @see #setDisallowedFields
@@ -526,8 +529,8 @@ public String[] getAllowedFields() {
526
529
* <p>More sophisticated matching can be implemented by overriding the
527
530
* {@link #isAllowed} method.
528
531
* <p>Alternatively, specify a list of <i>allowed</i> field patterns.
529
- * <p>Used for setter/field inject via {@link #bind(PropertyValues)}, and not
530
- * applicable to constructor initialization via {@link #construct(ValueResolver) },
532
+ * <p>Used for binding to fields with {@link #bind(PropertyValues)}, and not
533
+ * applicable to constructor binding via {@link #construct},
531
534
* which uses only the values it needs.
532
535
* @param disallowedFields array of disallowed field patterns
533
536
* @see #setAllowedFields
@@ -562,8 +565,8 @@ public String[] getDisallowedFields() {
562
565
* incoming property values, a corresponding "missing field" error
563
566
* will be created, with error code "required" (by the default
564
567
* binding error processor).
565
- * <p>Used for setter/field inject via {@link #bind(PropertyValues)}, and not
566
- * applicable to constructor initialization via {@link #construct(ValueResolver) },
568
+ * <p>Used for binding to fields with {@link #bind(PropertyValues)}, and not
569
+ * applicable to constructor binding via {@link #construct},
567
570
* which uses only the values it needs.
568
571
* @param requiredFields array of field names
569
572
* @see #setBindingErrorProcessor
@@ -586,6 +589,28 @@ public String[] getRequiredFields() {
586
589
return this .requiredFields ;
587
590
}
588
591
592
+ /**
593
+ * Configure a resolver to determine the name of the value to bind to a
594
+ * constructor parameter in {@link #construct}.
595
+ * <p>If not configured, or if the name cannot be resolved, by default
596
+ * {@link org.springframework.core.DefaultParameterNameDiscoverer} is used.
597
+ * @param nameResolver the resolver to use
598
+ * @since 6.1
599
+ */
600
+ public void setNameResolver (NameResolver nameResolver ) {
601
+ this .nameResolver = nameResolver ;
602
+ }
603
+
604
+ /**
605
+ * Return the {@link #setNameResolver configured} name resolver for
606
+ * constructor parameters.
607
+ * @since 6.1
608
+ */
609
+ @ Nullable
610
+ public NameResolver getNameResolver () {
611
+ return this .nameResolver ;
612
+ }
613
+
589
614
/**
590
615
* Set the strategy to use for resolving errors into message codes.
591
616
* Applies the given strategy to the underlying errors holder.
@@ -885,11 +910,19 @@ private Object createObject(ResolvableType objectType, String nestedPath, ValueR
885
910
Set <String > failedParamNames = new HashSet <>(4 );
886
911
887
912
for (int i = 0 ; i < paramNames .length ; i ++) {
888
- String paramPath = nestedPath + paramNames [i ];
913
+ MethodParameter param = MethodParameter .forFieldAwareConstructor (ctor , i , paramNames [i ]);
914
+ String lookupName = null ;
915
+ if (this .nameResolver != null ) {
916
+ lookupName = this .nameResolver .resolveName (param );
917
+ }
918
+ if (lookupName == null ) {
919
+ lookupName = paramNames [i ];
920
+ }
921
+
922
+ String paramPath = nestedPath + lookupName ;
889
923
Class <?> paramType = paramTypes [i ];
890
924
Object value = valueResolver .resolveValue (paramPath , paramType );
891
925
892
- MethodParameter param = MethodParameter .forFieldAwareConstructor (ctor , i , paramNames [i ]);
893
926
if (value == null && !BeanUtils .isSimpleValueType (param .nestedIfOptional ().getNestedParameterType ())) {
894
927
ResolvableType type = ResolvableType .forMethodParameter (param );
895
928
args [i ] = createObject (type , paramPath + "." , valueResolver );
@@ -1188,16 +1221,36 @@ else if (validator != null) {
1188
1221
1189
1222
1190
1223
/**
1191
- * Contract to resolve a value in {@link #construct(ValueResolver)}.
1224
+ * Strategy to determine the name of the value to bind to a method parameter.
1225
+ * Supported on constructor parameters with {@link #construct constructor
1226
+ * binding} which performs lookups via {@link ValueResolver#resolveValue}.
1227
+ */
1228
+ public interface NameResolver {
1229
+
1230
+ /**
1231
+ * Return the name to use for the given method parameter, or {@code null}
1232
+ * if unresolved. For constructor parameters, the name is determined via
1233
+ * {@link org.springframework.core.DefaultParameterNameDiscoverer} if
1234
+ * unresolved.
1235
+ */
1236
+ @ Nullable
1237
+ String resolveName (MethodParameter parameter );
1238
+
1239
+ }
1240
+
1241
+ /**
1242
+ * Strategy for {@link #construct constructor binding} to look up the values
1243
+ * to bind to a given constructor parameter.
1192
1244
*/
1193
1245
@ FunctionalInterface
1194
1246
public interface ValueResolver {
1195
1247
1196
1248
/**
1197
- * Look up the value for a constructor argument.
1198
- * @param name the argument name
1199
- * @param type the argument type
1200
- * @return the resolved value, possibly {@code null}
1249
+ * Resolve the value for the given name and target parameter type.
1250
+ * @param name the name to use for the lookup, possibly a nested path
1251
+ * for constructor parameters on nested objects
1252
+ * @param type the target type, based on the constructor parameter type
1253
+ * @return the resolved value, possibly {@code null} if none found
1201
1254
*/
1202
1255
@ Nullable
1203
1256
Object resolveValue (String name , Class <?> type );
@@ -1217,5 +1270,4 @@ public void registerCustomEditors(PropertyEditorRegistry registry) {
1217
1270
}
1218
1271
}
1219
1272
1220
-
1221
1273
}
0 commit comments