From 6e82bf05b66339ba8b8d36f06a71caacef1ad7ff Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Thu, 20 Jun 2024 11:09:12 +0100 Subject: [PATCH] Document list/map/array constructor data binding Closes gh-32426 --- .../pages/core/validation/beans-beans.adoc | 18 +++++++++++------- .../ann-methods/modelattrib-method-args.adoc | 4 ++++ .../ann-methods/modelattrib-method-args.adoc | 4 ++++ 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/framework-docs/modules/ROOT/pages/core/validation/beans-beans.adoc b/framework-docs/modules/ROOT/pages/core/validation/beans-beans.adoc index a68dbf43f8ac..6e395db14f99 100644 --- a/framework-docs/modules/ROOT/pages/core/validation/beans-beans.adoc +++ b/framework-docs/modules/ROOT/pages/core/validation/beans-beans.adoc @@ -27,15 +27,20 @@ The target class should have a single public constructor or a single non-public with arguments. If there are multiple constructors, then a default constructor if present is used. -By default, constructor parameter names are used to look up argument values, but you can -configure a `NameResolver`. Spring MVC and WebFlux both rely to allow customizing the name -of the value to bind through an `@BindParam` annotation on constructor parameters. +By default, argument values are looked up via constructor parameter names. Spring MVC and +WebFlux support a custom name mapping through the `@BindParam` annotation on constructor +parameters or fields if present. If necessary, you can also configure a `NameResolver` on +`DataBinder` to customize the argument name to use. xref:beans-beans-conventions[Type conversion] is applied as needed to convert user input. If the constructor parameter is an object, it is constructed recursively in the same manner, but through a nested property path. That means constructor binding creates both the target object and any objects it contains. +Constructor binding supports `List`, `Map`, and array arguments either converted from +a single string, e.g. comma-separated list, or based on indexed keys such as +`accounts[2].name` or `account[KEY].name`. + Binding and conversion errors are reflected in the `BindingResult` of the `DataBinder`. If the target is created successfully, then `target` is set to the created instance after the call to `construct`. @@ -90,13 +95,12 @@ details. The below table shows some examples of these conventions: | Indicates the nested property `name` of the property `account` that corresponds to (for example) the `getAccount().setName()` or `getAccount().getName()` methods. -| `account[2]` +| `accounts[2]` | Indicates the _third_ element of the indexed property `account`. Indexed properties can be of type `array`, `list`, or other naturally ordered collection. -| `account[COMPANYNAME]` -| Indicates the value of the map entry indexed by the `COMPANYNAME` key of the `account` `Map` - property. +| `accounts[KEY]` +| Indicates the value of the map entry indexed by the `KEY` value. |=== (This next section is not vitally important to you if you do not plan to work with diff --git a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc index a25ec608fe8d..65a1215b32d9 100644 --- a/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc +++ b/framework-docs/modules/ROOT/pages/web/webflux/controller/ann-methods/modelattrib-method-args.adoc @@ -81,6 +81,10 @@ NOTE: The `@BindParam` may also be placed on the fields that correspond to const parameters. While `@BindParam` is supported out of the box, you can also use a different annotation by setting a `DataBinder.NameResolver` on `DataBinder` +Constructor binding supports `List`, `Map`, and array arguments either converted from +a single string, e.g. comma-separated list, or based on indexed keys such as +`accounts[2].name` or `account[KEY].name`. + WebFlux, unlike Spring MVC, supports reactive types in the model, e.g. `Mono`. You can declare a `@ModelAttribute` argument with or without a reactive type wrapper, and it will be resolved accordingly to the actual value. diff --git a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc index 20b5ae436b02..466d9d7bd5a9 100644 --- a/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc +++ b/framework-docs/modules/ROOT/pages/web/webmvc/mvc-controller/ann-methods/modelattrib-method-args.adoc @@ -116,6 +116,10 @@ NOTE: The `@BindParam` may also be placed on the fields that correspond to const parameters. While `@BindParam` is supported out of the box, you can also use a different annotation by setting a `DataBinder.NameResolver` on `DataBinder` +Constructor binding supports `List`, `Map`, and array arguments either converted from +a single string, e.g. comma-separated list, or based on indexed keys such as +`accounts[2].name` or `account[KEY].name`. + In some cases, you may want access to a model attribute without data binding. For such cases, you can inject the `Model` into the controller and access it directly or, alternatively, set `@ModelAttribute(binding=false)`, as the following example shows: