Update documentation for data binding improvements
Closes gh-30952
This commit is contained in:
@@ -1,5 +1,50 @@
|
||||
[[beans-binding]]
|
||||
= Data Binding
|
||||
|
||||
Data binding is useful for binding user input to a target object where user input is a map
|
||||
with property paths as keys, following xref:beans-beans-conventions[JavaBeans conventions].
|
||||
`DataBinder` is the main class that supports this, and it provides two ways to bind user
|
||||
input:
|
||||
|
||||
- xref:beans-constructor-binding[Constructor binding] - bind user input to a public data
|
||||
constructor, looking up constructor argument values in the user input.
|
||||
- xref:beans-beans[Property binding] - bind user input to setters, matching keys from the
|
||||
the user input to properties of the target object structure.
|
||||
|
||||
You can apply both constructor and property binding or only one.
|
||||
|
||||
|
||||
[[beans-constructor-binding]]
|
||||
== Constructor Binding
|
||||
|
||||
To use constructor binding:
|
||||
|
||||
1. Create a `DataBinder` with `null` as the target object.
|
||||
2. Set `targetType` to the target class.
|
||||
3. Call `construct`.
|
||||
|
||||
The target class should have a single public constructor or a single non-public constructor
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
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`.
|
||||
|
||||
|
||||
|
||||
|
||||
[[beans-beans]]
|
||||
= Bean Manipulation and the `BeanWrapper`
|
||||
== Property Binding with `BeanWrapper`
|
||||
|
||||
The `org.springframework.beans` package adheres to the JavaBeans standard.
|
||||
A JavaBean is a class with a default no-argument constructor and that follows
|
||||
@@ -26,7 +71,7 @@ perform actions on that bean, such as setting and retrieving properties.
|
||||
|
||||
|
||||
[[beans-beans-conventions]]
|
||||
== Setting and Getting Basic and Nested Properties
|
||||
=== Setting and Getting Basic and Nested Properties
|
||||
|
||||
Setting and getting properties is done through the `setPropertyValue` and
|
||||
`getPropertyValue` overloaded method variants of `BeanWrapper`. See their Javadoc for
|
||||
@@ -192,7 +237,7 @@ Kotlin::
|
||||
|
||||
|
||||
[[beans-beans-conversion]]
|
||||
== Built-in `PropertyEditor` Implementations
|
||||
== ``PropertyEditor``'s
|
||||
|
||||
Spring uses the concept of a `PropertyEditor` to effect the conversion between an
|
||||
`Object` and a `String`. It can be handy
|
||||
@@ -378,7 +423,7 @@ Kotlin::
|
||||
|
||||
|
||||
[[beans-beans-conversion-customeditor-registration]]
|
||||
=== Registering Additional Custom `PropertyEditor` Implementations
|
||||
=== Custom ``PropertyEditor``'s
|
||||
|
||||
When setting bean properties as string values, a Spring IoC container ultimately uses
|
||||
standard JavaBeans `PropertyEditor` implementations to convert these strings to the complex type of the
|
||||
@@ -521,7 +566,7 @@ Finally, the following example shows how to use `CustomEditorConfigurer` to regi
|
||||
----
|
||||
|
||||
[[beans-beans-conversion-customeditor-registration-per]]
|
||||
==== Using `PropertyEditorRegistrar`
|
||||
=== `PropertyEditorRegistrar`
|
||||
|
||||
Another mechanism for registering property editors with the Spring container is to
|
||||
create and use a `PropertyEditorRegistrar`. This interface is particularly useful when
|
||||
|
||||
@@ -38,12 +38,44 @@ The `Pet` instance may be:
|
||||
request parameters. Argument names are determined through runtime-retained parameter
|
||||
names in the bytecode.
|
||||
|
||||
Once a model attribute instance is available, `WebDataBinder` binds request parameters to
|
||||
properties of the target `Object` with type conversion where necessary.
|
||||
For more on data binding and validation, see
|
||||
xref:web/webmvc/mvc-config/validation.adoc[Validation].
|
||||
For more on customizing data binding, see
|
||||
xref:web/webmvc/mvc-controller/ann-initbinder.adoc[DataBinder].
|
||||
By default, both constructor and property
|
||||
xref:core/validation/beans-beans.adoc#beans-binding[data binding] are applied. However,
|
||||
model object design requires careful consideration, and for security reasons it is
|
||||
recommended either to use an object tailored specifically for web binding, or to apply
|
||||
constructor binding only. If property binding must still be used, then _allowedFields_
|
||||
patterns should be set to limit which properties can be set. For further details on this
|
||||
and example configuration, see
|
||||
xref:web/webflux/controller/ann-initbinder.adoc#webflux-ann-initbinder-model-design[model design].
|
||||
|
||||
When using constructor binding, you can customize request parameter names through an
|
||||
`@BindParam` annotation. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
class Account {
|
||||
|
||||
private final String firstName;
|
||||
|
||||
public Account(@BindParam("first-name") String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
}
|
||||
----
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
class Account(@BindParam("first-name") val firstName: String)
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: The `@BindParam` may also be placed on the fields that correspond to constructor
|
||||
parameters. While `@BindParam` is supported out of the box, you can also use a
|
||||
different annotation by setting a `DataBinder.NameResolver` on `DataBinder`
|
||||
|
||||
WebFlux, unlike Spring MVC, supports reactive types in the model, e.g. `Mono<Account>`.
|
||||
You can declare a `@ModelAttribute` argument with or without a reactive type wrapper, and
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[[mvc-ann-initbinder]]
|
||||
= `DataBinder`
|
||||
= `@InitBinder`
|
||||
|
||||
[.small]#xref:web/webflux/controller/ann-initbinder.adoc[See equivalent in the Reactive stack]#
|
||||
|
||||
|
||||
@@ -73,12 +73,44 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
Once a model attribute instance is available, `WebDataBinder` binds request parameters to
|
||||
properties of the target `Object` with type conversion where necessary.
|
||||
For more on data binding and validation, see
|
||||
xref:web/webmvc/mvc-config/validation.adoc[Validation].
|
||||
For more on customizing data binding, see
|
||||
xref:web/webmvc/mvc-controller/ann-initbinder.adoc[DataBinder].
|
||||
By default, both constructor and property
|
||||
xref:core/validation/beans-beans.adoc#beans-binding[data binding] are applied. However,
|
||||
model object design requires careful consideration, and for security reasons it is
|
||||
recommended either to use an object tailored specifically for web binding, or to apply
|
||||
constructor binding only. If property binding must still be used, then _allowedFields_
|
||||
patterns should be set to limit which properties can be set. For further details on this
|
||||
and example configuration, see
|
||||
xref:web/webmvc/mvc-controller/ann-initbinder.adoc#mvc-ann-initbinder-model-design[model design].
|
||||
|
||||
When using constructor binding, you can customize request parameter names through an
|
||||
`@BindParam` annotation. For example:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
class Account {
|
||||
|
||||
private final String firstName;
|
||||
|
||||
public Account(@BindParam("first-name") String firstName) {
|
||||
this.firstName = firstName;
|
||||
}
|
||||
}
|
||||
----
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
class Account(@BindParam("first-name") val firstName: String)
|
||||
----
|
||||
======
|
||||
|
||||
NOTE: The `@BindParam` may also be placed on the fields that correspond to constructor
|
||||
parameters. While `@BindParam` is supported out of the box, you can also use a
|
||||
different annotation by setting a `DataBinder.NameResolver` on `DataBinder`
|
||||
|
||||
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,
|
||||
|
||||
@@ -38,9 +38,16 @@ the properties required for the input:
|
||||
}
|
||||
----
|
||||
|
||||
If a dedicated model object is not feasible, we strongy recommend registering
|
||||
`allowedFields` patterns (case sensitive) on `WebDataBinder` in order to prevent other
|
||||
properties from being set. For example:
|
||||
Another good practice is to apply
|
||||
xref:core/validation/beans-beans.adoc#beans-constructor-binding[constructor binding],
|
||||
which uses only the request parameters it needs for constructor arguments, and any other
|
||||
input is ignored. This is in contrast to property binding which by default binds every
|
||||
request parameter for which there is a matching property.
|
||||
|
||||
If neither a dedicated model object nor constructor binding is sufficient, and you must
|
||||
use property binding, we strongy recommend registering `allowedFields` patterns (case
|
||||
sensitive) on `WebDataBinder` in order to prevent unexpected properties from being set.
|
||||
For example:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@@ -60,3 +67,25 @@ properties from being set. For example:
|
||||
You can also register `disallowedFields` patterns (case insensitive). However,
|
||||
"allowed" configuration is preferred over "disallowed" as it is more explicit and less
|
||||
prone to mistakes.
|
||||
|
||||
By default, constructor and property binding are both used. If you want to use
|
||||
constructor binding only, you can set the `declarativeBinding` flag on `WebDataBinder`
|
||||
through an `@InitBinder` method either locally within a controller or globally through an
|
||||
`@ControllerAdvice`. Turning this flag on ensures that only constructor binding is used
|
||||
and that property binding is not used unless `allowedFields` patterns are configured.
|
||||
For example:
|
||||
|
||||
[source,java,indent=0,subs="verbatim,quotes"]
|
||||
----
|
||||
@Controller
|
||||
public class MyController {
|
||||
|
||||
@InitBinder
|
||||
void initBinder(WebDataBinder binder) {
|
||||
binder.setDeclarativeBinding(true);
|
||||
}
|
||||
|
||||
// @RequestMapping methods, etc.
|
||||
|
||||
}
|
||||
----
|
||||
|
||||
Reference in New Issue
Block a user