WebFlux @ModelAttribute coverage in reference

Issue: SPR-16040
This commit is contained in:
Rossen Stoyanchev 2018-01-15 17:46:31 -05:00
parent 43d3abdfd5
commit 437c33ba42
2 changed files with 447 additions and 134 deletions

View File

@ -1225,6 +1225,263 @@ Type conversion is applied automatically if the target method parameter type is
`String`. See <<mvc-ann-typeconversion>>.
[[webflux-ann-modelattrib-method-args]]
==== @ModelAttribute
[.small]#<<web.adoc#mvc-ann-modelattrib-method-args,Same in Spring MVC>>#
Use the `@ModelAttribute` annotation on a method argument to access an attribute from the
model, or have it instantiated if not present. The model attribute is also overlaid with
values query parameters for form fields whose names match to field names. This is
referred to as data binding and it saves you from having to deal with parsing and
converting individual query parameters and form fields. For example:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(**@ModelAttribute Pet pet**) { }
----
The `Pet` instance above is resolved as follows:
* From the model if already added via <<webflux-ann-modelattrib-methods>>.
* From the HTTP session via <<webflux-ann-sessionattributes>>.
* From the invocation of a default constructor.
* From the invocation of a "primary constructor" with arguments matching to query
parameters or form fields; argument names are determined via JavaBeans
`@ConstructorProperties` or via runtime-retained parameter names in the bytecode.
After the model attribute instance is obtained, data binding is applied. The
`WebExchangeDataBinder` class matches names of query parameters and form fields to field
names on the target Object. Matching fields are populated after type conversion is applied
where necessary. For more on data binding (and validation) see
<<core.adoc#validation, Validation>>. For more on customizing data binding see
<<webflux-ann-initbinder>>.
Data binding may result in errors. By default a `WebExchangeBindException` is raised but
to check for such errors in the controller method, add a `BindingResult` argument
immediately next to the `@ModelAttribute` as shown below:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
----
Validation can be applied automatically after data binding by adding the
`javax.validation.Valid` annotation or Spring's `@Validated` annotation (also see
<<core.adoc#validation-beanvalidation, Bean validation>> and
<<core.adoc#validation, Spring validation>>). For example:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(**@Valid @ModelAttribute("pet") Pet pet**, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
----
Spring WebFlux, unlike Spring MVC, supports reactive types in the model, e.g.
`Mono<Account>` or `io.reactivex.Single<Account>`. An `@ModelAttribute` argument can be
declared with or without a reactive type wrapper, and it will be resolved accordingly,
to the actual value if necessary. Note however that in order to use a `BindingResult`
argument, you must declare the `@ModelAttribute` argument before it without a reactive
type wrapper, as shown earlier. Alternatively, you can handle any errors through the
reactive type:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public Mono<String> processSubmit(@Valid @ModelAttribute("pet") Mono<Pet> petMono) {
return petMono
.flatMap(pet -> {
// ...
})
.onErrorResume(ex -> {
// ...
});
}
----
[[webflux-ann-sessionattributes]]
==== @SessionAttributes
[.small]#<<web.adoc#mvc-ann-sessionattributes,Same in Spring MVC>>#
`@SessionAttributes` is used to store model attributes in the `WebSession` between
requests. It is a type-level annotation that declares session attributes used by a
specific controller. This will typically list the names of model attributes or types of
model attributes which should be transparently stored in the session for subsequent
requests to access.
For example:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Controller
**@SessionAttributes("pet")**
public class EditPetForm {
// ...
}
----
On the first request when a model attribute with the name "pet" is added to the model,
it is automatically promoted to and saved in the `WebSession`. It remains there until
another controller method uses a `SessionStatus` method argument to clear the storage:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Controller
**@SessionAttributes("pet")**
public class EditPetForm {
// ...
@PostMapping("/pets/{id}")
public String handle(Pet pet, BindingResult errors, SessionStatus status) {
if (errors.hasErrors) {
// ...
}
status.setComplete();
// ...
}
}
}
----
[[webflux-ann-sessionattribute]]
==== @SessionAttribute
[.small]#<<web.adoc#mvc-ann-sessionattribute,Same in Spring MVC>>#
If you need access to pre-existing session attributes that are managed globally,
i.e. outside the controller (e.g. by a filter), and may or may not be present
use the `@SessionAttribute` annotation on a method parameter:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@RequestMapping("/")
public String handle(**@SessionAttribute** User user) {
// ...
}
----
For use cases that require adding or removing session attributes consider injecting
`WebSession` into the controller method.
For temporary storage of model attributes in the session as part of a controller
workflow consider using `SessionAttributes` as described in
<<webflux-ann-sessionattributes>>.
[[webflux-ann-modelattrib-methods]]
=== Model Methods
[.small]#<<web.adoc#mvc-ann-modelattrib-methods,Same in Spring MVC>>#
The `@ModelAttribute` annotation can be used on `@RequestMapping`
<<webflux-ann-modelattrib-method-args,method arguments>> to create or access an Object
from the model and bind it to the request. `@ModelAttribute` can also be used as a
method-level annotation on controller methods whose purpose is not to handle requests
but to add commonly needed model attributes prior to request handling.
A controller can have any number of `@ModelAttribute` methods. All such methods are
invoked before `@RequestMapping` methods in the same controller. A `@ModelAttribute`
method can also be shared across controllers via `@ControllerAdvice`. See the section on
<<webflux-ann-controller-advice>> for more details.
`@ModelAttribute` methods have flexible method signatures. They support many of the same
arguments as `@RequestMapping` methods except for `@ModelAttribute` itself nor anything
related to the request body.
An example `@ModelAttribute` method:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
model.addAttribute(accountRepository.findAccount(number));
// add more ...
}
----
To add one attribute only:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@ModelAttribute
public Account addAccount(@RequestParam String number) {
return accountRepository.findAccount(number);
}
----
[NOTE]
====
When a name is not explicitly specified, a default name is chosen based on the Object
type as explained in the Javadoc for
{api-spring-framework}/core/Conventions.html[Conventions].
You can always assign an explicit name by using the overloaded `addAttribute` method or
through the name attribute on `@ModelAttribute` (for a return value).
====
Spring WebFlux, unlike Spring MVC, explicitly supports reactive types in the model,
e.g. `Mono<Account>` or `io.reactivex.Single<Account>`. Such asynchronous model
attributes may be transparently resolved (and the model updated) to their actual values
at the time of `@RequestMapping` invocation, providing a `@ModelAttribute` argument is
declared without a wrapper, for example:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@ModelAttribute
public void addAccount(@RequestParam String number) {
Mono<Account> accountMono = accountRepository.findAccount(number);
model.addAttribute("account", accountMono);
}
@PostMapping("/accounts")
public String handle(@ModelAttribute Account account, BindingResult errors) {
// ...
}
----
In addition any model attributes that have a reactive type wrapper are resolved to their
actual values (and the model updated) just prior to view rendering.
`@ModelAttribute` can also be used as a method-level annotation on `@RequestMapping`
methods in which case the return value of the `@RequestMapping` method is interpreted as a
model attribute. This is typically not required, as it is the default behavior in HTML
controllers, unless the return value is a `String` which would otherwise be interpreted
as a view name. `@ModelAttribute` can also help to customize the model attribute name:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@GetMapping("/accounts/{id}")
@ModelAttribute("myAccount")
public Account handle() {
// ...
return account;
}
----
[[webflux-ann-initbinder]]
=== Binder Methods
@ -1284,6 +1541,53 @@ controller-specific ``Formatter``'s:
[[webflux-ann-controller-advice]]
=== Controller Advice
[.small]#<<web.adoc#mvc-ann-controller-advice,Same in Spring MVC>>#
Typically `@ExceptionHandler`, `@InitBinder`, and `@ModelAttribute` methods apply within
the `@Controller` class (or class hierarchy) they are declared in. If you want such
methods to apply more globally, across controllers, you can declare them in a class
marked with `@ControllerAdvice` or `@RestControllerAdvice`.
`@ControllerAdvice` is marked with `@Component` which means such classes can be registered
as Spring beans via <<core.adoc#beans-java-instantiating-container-scan,component scanning>>.
`@RestControllerAdvice` is also a meta-annotation marked with both `@ControllerAdvice` and
`@ResponseBody` which essentially means `@ExceptionHandler` methods are rendered to the
response body via message conversion (vs view resolution/template rendering).
On startup, the infrastructure classes for `@RequestMapping` and `@ExceptionHandler` methods
detect Spring beans of type `@ControllerAdvice`, and then apply their methods at runtime.
Global `@ExceptionHandler` methods (from an `@ControllerAdvice`) are applied *after* local
ones (from the `@Controller`). By contrast global `@ModelAttribute` and `@InitBinder`
methods are applied *before* local ones.
By default `@ControllerAdvice` methods apply to every request, i.e. all controllers, but
you can narrow that down to a subset of controllers via attributes on the annotation:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}
// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}
// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}
----
Keep in mind the above selectors are evaluated at runtime and may negatively impact
performance if used extensively. See the
{api-spring-framework}/web/bind/annotation/ControllerAdvice.html[@ControllerAdvice]
Javadoc for more details.
include::webflux-functional.adoc[leveloffset=+1]

View File

@ -1881,16 +1881,13 @@ Type conversion is applied automatically if the target method parameter type is
[[mvc-ann-modelattrib-method-args]]
==== @ModelAttribute
[.small]#<<web-reactive.adoc#webflux-ann-modelattrib-method-args,Same in Spring WebFlux>>#
As explained in the previous section `@ModelAttribute` can be used on methods or on
method arguments. This section explains its usage on method arguments.
An `@ModelAttribute` on a method argument indicates the argument should be retrieved
from the model. If not present in the model, the argument should be instantiated first
and then added to the model. Once present in the model, the argument's fields should be
populated from all request parameters that have matching names. This is known as data
binding in Spring MVC, a very useful mechanism that saves you from having to parse each
form field individually.
Use the `@ModelAttribute` annotation on a method argument to access an attribute from the
model, or have it instantiated if not present. The model attribute is also overlaid with
values from HTTP Servlet request parameters whose names match to field names. This is
referred to as data binding and it saves you from having to deal with parsing and
converting individual query parameters and form fields. For example:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -1899,20 +1896,21 @@ form field individually.
public String processSubmit(**@ModelAttribute Pet pet**) { }
----
Given the above example where can the Pet instance come from? There are several options:
The `Pet` instance above is resolved as follows:
* It may already be in the model due to use of `@SessionAttributes` -- see
<<mvc-ann-sessionattrib>>.
* It may already be in the model due to an `@ModelAttribute` method in the same
controller -- as explained in the previous section.
* It may be retrieved based on a URI template variable and type converter (explained in
more detail below).
* It may be instantiated using its default constructor.
* From the model if already added via <<mvc-ann-modelattrib-methods>>.
* From the HTTP session via <<mvc-ann-sessionattributes>>.
* From a URI path variable passed through a `Converter` (example below).
* From the invocation of a default constructor.
* From the invocation of a "primary constructor" with arguments matching to Servlet
request parameters; argument names are determined via JavaBeans
`@ConstructorProperties` or via runtime-retained parameter names in the bytecode.
An `@ModelAttribute` method is a common way to retrieve an attribute from the
database, which may optionally be stored between requests through the use of
`@SessionAttributes`. In some cases it may be convenient to retrieve the attribute by
using an URI template variable and a type converter. Here is an example:
While it is common to use a <<mvc-ann-modelattrib-methods>> to populate the model with
attributes, one other alternative is to rely on a `Converter<String, T>` in combination
with a URI path variable convention. In the example below the model attribute name
"account" matches the URI path variable "account" and the `Account` is loaded by passing
the `String` account number through a registered `Converter<String, Account>`:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -1923,22 +1921,16 @@ using an URI template variable and a type converter. Here is an example:
}
----
In this example the name of the model attribute (i.e. "account") matches the name of a
URI template variable. If you register `Converter<String, Account>` that can turn the
`String` account value into an `Account` instance, then the above example will work
without the need for an `@ModelAttribute` method.
The next step is data binding. The `WebDataBinder` class matches request parameter names
-- including query string parameters and form fields -- to model attribute fields by
name. Matching fields are populated after type conversion (from String to the target
field type) has been applied where necessary. Data binding and validation are covered in
<<core.adoc#validation, Validation>>.
Customizing the data binding process for a controller level is covered in
After the model attribute instance is obtained, data binding is applied. The
`WebDataBinder` class matches Servlet request parameter names (query parameters and form
fields) to field names on the target Object. Matching fields are populated after type
conversion is applied where necessary. For more on data binding (and validation) see
<<core.adoc#validation, Validation>>. For more on customizing data binding see
<<mvc-ann-initbinder>>.
As a result of data binding there may be errors such as missing required fields or type
conversion errors. To check for such errors add a `BindingResult` argument immediately
following the `@ModelAttribute` argument:
Data binding may result in errors. By default a `BindException` is raised but to check
for such errors in the controller method, add a `BindingResult` argument immediately next
to the `@ModelAttribute` as shown below:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -1952,13 +1944,9 @@ following the `@ModelAttribute` argument:
}
----
With a `BindingResult` you can check if errors were found in which case it's common to
render the same form where the errors can be shown with the help of Spring's `<errors>`
form tag.
Note that in some cases it may be useful to gain access to an attribute in the
model without data binding. For such cases you may inject the `Model` into the
controller or alternatively use the `binding` flag on the annotation:
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 shown below:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -1980,26 +1968,10 @@ controller or alternatively use the `binding` flag on the annotation:
}
----
In addition to data binding you can also invoke validation using your own custom
validator passing the same `BindingResult` that was used to record data binding errors.
That allows for data binding and validation errors to be accumulated in one place and
subsequently reported back to the user:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(**@ModelAttribute("pet") Pet pet**, BindingResult result) {
new PetValidator().validate(pet, result);
if (result.hasErrors()) {
return "petForm";
}
// ...
}
----
Or you can have validation invoked automatically by adding the JSR-303 `@Valid`
annotation:
Validation can be applied automatically after data binding by adding the
`javax.validation.Valid` annotation or Spring's `@Validated` annotation (also see
<<core.adoc#validation-beanvalidation, Bean validation>> and
<<core.adoc#validation, Spring validation>>). For example:
[source,java,indent=0]
[subs="verbatim,quotes"]
@ -2013,9 +1985,6 @@ annotation:
}
----
See <<core.adoc#validation-beanvalidation, Bean validation>> and
<<core.adoc#validation, Spring validation>> for details on how to configure and use validation.
[[mvc-multipart-forms]]
==== File upload
@ -2098,29 +2067,56 @@ the method parameter:
[[mvc-ann-sessionattributes]]
==== @SessionAttributes
[.small]#<<web-reactive.adoc#webflux-ann-sessionattributes,Same in Spring WebFlux>>#
`@SessionAttributes` is used to store model attributes in the HTTP session between
`@SessionAttributes` is used to store model attributes in the HTTP Servlet session between
requests. It is a type-level annotation that declares session attributes used by a
specific handler. This will typically list the names of model attributes or types of
model attributes which should be transparently stored in the session or some
conversational storage, serving as form-backing beans between subsequent requests.
specific controller. This will typically list the names of model attributes or types of
model attributes which should be transparently stored in the session for subsequent
requests to access.
The following code snippet shows the usage of this annotation, specifying the model
attribute name:
For example:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Controller
@RequestMapping("/editPet.do")
**@SessionAttributes("pet")**
public class EditPetForm {
// ...
}
----
On the first request when a model attribute with the name "pet" is added to the model,
it is automatically promoted to and saved in the HTTP Servlet session. It remains there
until another controller method uses a `SessionStatus` method argument to clear the
storage:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@Controller
**@SessionAttributes("pet")**
public class EditPetForm {
// ...
@PostMapping("/pets/{id}")
public String handle(Pet pet, BindingResult errors, SessionStatus status) {
if (errors.hasErrors) {
// ...
}
status.setComplete();
// ...
}
}
}
----
[[mvc-ann-sessionattribute]]
==== @SessionAttribute
[.small]#<<web-reactive.adoc#webflux-ann-sessionattribute,Same in Spring WebFlux>>#
If you need access to pre-existing session attributes that are managed globally,
i.e. outside the controller (e.g. by a filter), and may or may not be present
@ -2141,7 +2137,7 @@ For use cases that require adding or removing session attributes consider inject
For temporary storage of model attributes in the session as part of a controller
workflow consider using `SessionAttributes` as described in
<<mvc-ann-sessionattrib>>.
<<mvc-ann-sessionattributes>>.
[[mvc-ann-requestattrib]]
@ -2569,69 +2565,74 @@ customized through `jsonpParameterNames` property.
[[mvc-ann-modelattrib-methods]]
=== Model Methods
[.small]#<<web-reactive.adoc#webflux-ann-modelattrib-methods,Same in Spring WebFlux>>#
The `@ModelAttribute` annotation can be used on methods or on method arguments. This
section explains its usage on methods while the next section explains its usage on
method arguments.
The `@ModelAttribute` annotation can be used on `@RequestMapping`
<<mvc-ann-modelattrib-method-args,method arguments>> to create or access an Object
from the model and bind it to the request. `@ModelAttribute` can also be used as a
method-level annotation on controller methods whose purpose is not to handle requests
but to add commonly needed model attributes prior to request handling.
An `@ModelAttribute` on a method indicates the purpose of that method is to add one or
more model attributes. Such methods support the same argument types as `@RequestMapping`
methods but cannot be mapped directly to requests. Instead `@ModelAttribute` methods in
a controller are invoked before `@RequestMapping` methods, within the same controller. A
couple of examples:
A controller can have any number of `@ModelAttribute` methods. All such methods are
invoked before `@RequestMapping` methods in the same controller. A `@ModelAttribute`
method can also be shared across controllers via `@ControllerAdvice`. See the section on
<<mvc-ann-controller-advice>> for more details.
`@ModelAttribute` methods have flexible method signatures. They support many of the same
arguments as `@RequestMapping` methods except for `@ModelAttribute` itself nor anything
related to the request body.
An example `@ModelAttribute` method:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
// Add one attribute
// The return value of the method is added to the model under the name "account"
// You can customize the name via @ModelAttribute("myAccount")
@ModelAttribute
public Account addAccount(@RequestParam String number) {
return accountManager.findAccount(number);
}
// Add multiple attributes
@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {
model.addAttribute(accountManager.findAccount(number));
model.addAttribute(accountRepository.findAccount(number));
// add more ...
}
----
`@ModelAttribute` methods are used to populate the model with commonly needed attributes
for example to fill a drop-down with states or with pet types, or to retrieve a command
object like Account in order to use it to represent the data on an HTML form. The latter
case is further discussed in the next section.
To add one attribute only:
Note the two styles of `@ModelAttribute` methods. In the first, the method adds an
attribute implicitly by returning it. In the second, the method accepts a `Model` and
adds any number of model attributes to it. You can choose between the two styles
depending on your needs.
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@ModelAttribute
public Account addAccount(@RequestParam String number) {
return accountRepository.findAccount(number);
}
----
A controller can have any number of `@ModelAttribute` methods. All such methods are
invoked before `@RequestMapping` methods of the same controller.
`@ModelAttribute` methods can also be defined in an ``@ControllerAdvice``-annotated class
and such methods apply to many controllers. See the <<mvc-ann-controller-advice>> section
for more details.
[TIP]
[NOTE]
====
What happens when a model attribute name is not explicitly specified? In such cases a
default name is assigned to the model attribute based on its type. For example if the
method returns an object of type `Account`, the default name used is "account". You can
change that through the value of the `@ModelAttribute` annotation. If adding attributes
directly to the `Model`, use the appropriate overloaded `addAttribute(..)` method -
i.e., with or without an attribute name.
When a name is not explicitly specified, a default name is chosen based on the Object
type as explained in the Javadoc for
{api-spring-framework}/core/Conventions.html[Conventions].
You can always assign an explicit name by using the overloaded `addAttribute` method or
through the name attribute on `@ModelAttribute` (for a return value).
====
The `@ModelAttribute` annotation can be used on `@RequestMapping` methods as well. In
that case the return value of the `@RequestMapping` method is interpreted as a model
attribute rather than as a view name. The view name is then derived based on view name
conventions instead, much like for methods returning `void` -- see <<mvc-coc-r2vnt>>.
`@ModelAttribute` can also be used as a method-level annotation on `@RequestMapping`
methods in which case the return value of the `@RequestMapping` method is interpreted as a
model attribute. This is typically not required, as it is the default behavior in HTML
controllers, unless the return value is a `String` which would otherwise be interpreted
as a view name (also see <<mvc-coc-r2vnt>>). `@ModelAttribute` can also help to customize
the model attribute name:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
@GetMapping("/accounts/{id}")
@ModelAttribute("myAccount")
public Account handle() {
// ...
return account;
}
----
@ -2696,40 +2697,48 @@ controller-specific ``Formatter``'s:
[[mvc-ann-controller-advice]]
=== Controller Advice
[.small]#<<web-reactive.adoc#webflux-ann-controller-advice,Same in Spring WebFlux>>#
The `@ControllerAdvice` annotation is a component annotation allowing implementation
classes to be auto-detected through classpath scanning. It is automatically enabled when
using the MVC namespace or the MVC Java config.
Typically `@ExceptionHandler`, `@InitBinder`, and `@ModelAttribute` methods apply within
the `@Controller` class (or class hierarchy) they are declared in. If you want such
methods to apply more globally, across controllers, you can declare them in a class
marked with `@ControllerAdvice` or `@RestControllerAdvice`.
Classes annotated with `@ControllerAdvice` can contain `@ExceptionHandler`,
`@InitBinder`, and `@ModelAttribute` annotated methods, and these methods will apply to
`@RequestMapping` methods across all controller hierarchies as opposed to the controller
hierarchy within which they are declared.
`@ControllerAdvice` is marked with `@Component` which means such classes can be registered
as Spring beans via <<core.adoc#beans-java-instantiating-container-scan,component scanning>>.
`@RestControllerAdvice` is also a meta-annotation marked with both `@ControllerAdvice` and
`@ResponseBody` which essentially means `@ExceptionHandler` methods are rendered to the
response body via message conversion (vs view resolution/template rendering).
`@RestControllerAdvice` is an alternative where `@ExceptionHandler` methods
assume `@ResponseBody` semantics by default.
On startup, the infrastructure classes for `@RequestMapping` and `@ExceptionHandler` methods
detect Spring beans of type `@ControllerAdvice`, and then apply their methods at runtime.
Global `@ExceptionHandler` methods (from an `@ControllerAdvice`) are applied *after* local
ones (from the `@Controller`). By contrast global `@ModelAttribute` and `@InitBinder`
methods are applied *before* local ones.
Both `@ControllerAdvice` and `@RestControllerAdvice` can target a subset of controllers:
By default `@ControllerAdvice` methods apply to every request, i.e. all controllers, but
you can narrow that down to a subset of controllers via attributes on the annotation:
[source,java,indent=0]
[subs="verbatim,quotes"]
----
// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class AnnotationAdvice {}
public class ExampleAdvice1 {}
// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class BasePackageAdvice {}
public class ExampleAdvice2 {}
// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class AssignableTypesAdvice {}
public class ExampleAdvice3 {}
----
See the
Keep in mind the above selectors are evaluated at runtime and may negatively impact
performance if used extensively. See the
{api-spring-framework}/web/bind/annotation/ControllerAdvice.html[@ControllerAdvice]
javadoc for more details.
Javadoc for more details.