Reference docs: update async request content
Issue: SPR-16203
This commit is contained in:
parent
aee8a9c97b
commit
4b861aeae6
|
@ -3153,19 +3153,47 @@ or in a JSP:
|
||||||
[[mvc-ann-async]]
|
[[mvc-ann-async]]
|
||||||
== Async Requests
|
== Async Requests
|
||||||
|
|
||||||
|
Spring MVC has an extensive integration with the Servlet 3.0 asynchronous request
|
||||||
|
processing. <<mvc-ann-async-deferredresult>> and <<mvc-ann-async-callable>>
|
||||||
|
provide basic support for producing return values asynchronously. Controllers can produce
|
||||||
|
<<mvc-ann-async-http-streaming,response streams>> including
|
||||||
|
<<mvc-ann-async-sse,SSE>> and <<mvc-ann-async-output-stream,raw data>>. Controllers can
|
||||||
|
use reactive clients and return <<mvc-ann-async-reactive-types,reactive return types>>
|
||||||
|
to Spring MVC for response handling.
|
||||||
|
|
||||||
|
|
||||||
[[mvc-ann-async-processing]]
|
|
||||||
=== Processing
|
|
||||||
|
|
||||||
Spring MVC 3.2 introduced Servlet 3 based asynchronous request processing. Instead of
|
[[mvc-ann-async-deferredresult]]
|
||||||
returning a value, as usual, a controller method can now return a
|
=== `DeferredResult`
|
||||||
`java.util.concurrent.Callable` and produce the return value from a Spring MVC managed thread.
|
|
||||||
Meanwhile the main Servlet container thread is exited and released and allowed to process other
|
Once the asynchronous request processing feature is
|
||||||
requests. Spring MVC invokes the `Callable` in a separate thread with the help of a
|
<<mvc-ann-async-configuration,enabled>> in the Servlet container, controller methods can
|
||||||
`TaskExecutor` and when the `Callable` returns, the request is dispatched back to the
|
wrap any supported controller method return value with `DeferredResult`:
|
||||||
Servlet container to resume processing using the value returned by the `Callable`. Here
|
|
||||||
is an example of such a controller method:
|
[source,java,indent=0]
|
||||||
|
[subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
@GetMapping("/quotes")
|
||||||
|
@ResponseBody
|
||||||
|
public DeferredResult<String> quotes() {
|
||||||
|
DeferredResult<String> deferredResult = new DeferredResult<String>();
|
||||||
|
// Save the deferredResult somewhere..
|
||||||
|
return deferredResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// From some other thread...
|
||||||
|
deferredResult.setResult(data);
|
||||||
|
----
|
||||||
|
|
||||||
|
The controller can produce the return value asynchronously, from a different thread, for
|
||||||
|
example in response to an external event (JMS message), a scheduled task, or other.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[mvc-ann-async-callable]]
|
||||||
|
=== `Callable`
|
||||||
|
|
||||||
|
A controller may also wrap any supported return value with `java.util.concurrent.Callable`:
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0]
|
||||||
[subs="verbatim,quotes"]
|
[subs="verbatim,quotes"]
|
||||||
|
@ -3183,30 +3211,15 @@ is an example of such a controller method:
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
Another option is for the controller method to return an instance of `DeferredResult`. In this
|
The return value will then be obtained by executing the the given task through the
|
||||||
case the return value will also be produced from any thread, i.e. one that
|
<<mvc-ann-async-configuration-spring-mvc,configured>> `TaskExecutor`.
|
||||||
is not managed by Spring MVC. For example the result may be produced in response to some
|
|
||||||
external event such as a JMS message, a scheduled task, and so on. Here is an example
|
|
||||||
of such a controller method:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
|
||||||
[subs="verbatim,quotes"]
|
|
||||||
----
|
|
||||||
@RequestMapping("/quotes")
|
|
||||||
@ResponseBody
|
|
||||||
public DeferredResult<String> quotes() {
|
|
||||||
DeferredResult<String> deferredResult = new DeferredResult<String>();
|
|
||||||
// Save the deferredResult somewhere..
|
|
||||||
return deferredResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
// In some other thread...
|
|
||||||
deferredResult.setResult(data);
|
|
||||||
----
|
|
||||||
|
|
||||||
This may be difficult to understand without any knowledge of the Servlet 3.0
|
[[mvc-ann-async-processing]]
|
||||||
asynchronous request processing features. It would certainly help to read up
|
=== Processing
|
||||||
on that. Here are a few basic facts about the underlying mechanism:
|
|
||||||
|
Here is a very concise overview of Servlet asynchronous request processing:
|
||||||
|
|
||||||
* A `ServletRequest` can be put in asynchronous mode by calling `request.startAsync()`.
|
* A `ServletRequest` can be put in asynchronous mode by calling `request.startAsync()`.
|
||||||
The main effect of doing so is that the Servlet, as well as any Filters, can exit but
|
The main effect of doing so is that the Servlet, as well as any Filters, can exit but
|
||||||
|
@ -3219,98 +3232,83 @@ on that. Here are a few basic facts about the underlying mechanism:
|
||||||
be used to distinguish between processing the initial request, an async
|
be used to distinguish between processing the initial request, an async
|
||||||
dispatch, a forward, and other dispatcher types.
|
dispatch, a forward, and other dispatcher types.
|
||||||
|
|
||||||
With the above in mind, the following is the sequence of events for async request
|
`DeferredResult` processing:
|
||||||
processing with a `Callable`:
|
|
||||||
|
|
||||||
* Controller returns a `Callable`.
|
|
||||||
* Spring MVC starts asynchronous processing and submits the `Callable` to
|
|
||||||
a `TaskExecutor` for processing in a separate thread.
|
|
||||||
* The `DispatcherServlet` and all Filter's exit the Servlet container thread
|
|
||||||
but the response remains open.
|
|
||||||
* The `Callable` produces a result and Spring MVC dispatches the request back
|
|
||||||
to the Servlet container to resume processing.
|
|
||||||
* The `DispatcherServlet` is invoked again and processing resumes with the
|
|
||||||
asynchronously produced result from the `Callable`.
|
|
||||||
|
|
||||||
The sequence for `DeferredResult` is very similar except it's up to the
|
|
||||||
application to produce the asynchronous result from any thread:
|
|
||||||
|
|
||||||
* Controller returns a `DeferredResult` and saves it in some in-memory
|
* Controller returns a `DeferredResult` and saves it in some in-memory
|
||||||
queue or list where it can be accessed.
|
queue or list where it can be accessed.
|
||||||
* Spring MVC starts async processing.
|
* Spring MVC calls `request.startAsync()`.
|
||||||
* The `DispatcherServlet` and all configured Filter's exit the request
|
* Meanwhile the `DispatcherServlet` and all configured Filter's exit the request
|
||||||
processing thread but the response remains open.
|
processing thread but the response remains open.
|
||||||
* The application sets the `DeferredResult` from some thread and Spring MVC
|
* The application sets the `DeferredResult` from some thread and Spring MVC
|
||||||
dispatches the request back to the Servlet container.
|
dispatches the request back to the Servlet container.
|
||||||
* The `DispatcherServlet` is invoked again and processing resumes with the
|
* The `DispatcherServlet` is invoked again and processing resumes with the
|
||||||
asynchronously produced result.
|
asynchronously produced return value.
|
||||||
|
|
||||||
For further background on the motivation for async request processing and
|
`Callable` processing:
|
||||||
when or why to use it please read
|
|
||||||
https://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support[this
|
* Controller returns a `Callable`.
|
||||||
blog post series].
|
* Spring MVC calls `request.startAsync()` and submits the `Callable` to
|
||||||
|
a `TaskExecutor` for processing in a separate thread.
|
||||||
|
* Meanwhile the `DispatcherServlet` and all Filter's exit the Servlet container thread
|
||||||
|
but the response remains open.
|
||||||
|
* Eventually the `Callable` produces a result and Spring MVC dispatches the request back
|
||||||
|
to the Servlet container to complete processing.
|
||||||
|
* The `DispatcherServlet` is invoked again and processing resumes with the
|
||||||
|
asynchronously produced return value from the `Callable`.
|
||||||
|
|
||||||
|
For further background and context you can also read
|
||||||
|
https://spring.io/blog/2012/05/07/spring-mvc-3-2-preview-introducing-servlet-3-async-support[the
|
||||||
|
blog posts] that introduced asynchronous request processing support in Spring MVC 3.2.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[mvc-ann-async-exceptions]]
|
[[mvc-ann-async-exceptions]]
|
||||||
=== Exception handling
|
=== Exception handling
|
||||||
|
|
||||||
What happens if a `Callable` returned from a controller method raises an
|
When using a `DeferredResult` you can choose whether to call `setResult` or
|
||||||
Exception while being executed? The short answer is the same as what happens
|
`setErrorResult` with an exception. In both cases Spring MVC dispatches the request back
|
||||||
when a controller method raises an exception. It goes through the regular
|
to the Servlet container to complete processing. It is then treated either as if the
|
||||||
exception handling mechanism. The longer explanation is that when a `Callable`
|
controller method returned the given value, or as if it produced the given exception.
|
||||||
raises an Exception Spring MVC dispatches to the Servlet container with
|
The exception then goes through the regular exception handling mechanism, e.g. invoking
|
||||||
the `Exception` as the result and that leads to resume request processing
|
`@ExceptionHandler` methods.
|
||||||
with the `Exception` instead of a controller method return value.
|
|
||||||
When using a `DeferredResult` you have a choice whether to call
|
When using `Callable`, similar processing logic follows. The main difference being that
|
||||||
`setResult` or `setErrorResult` with an `Exception` instance.
|
the result is returned from the `Callable` or an exception is raised by it.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[mvc-ann-async-interception]]
|
[[mvc-ann-async-interception]]
|
||||||
=== Async interceptors
|
=== Interception
|
||||||
|
|
||||||
A `HandlerInterceptor` can also implement `AsyncHandlerInterceptor` in order
|
``HandlerInterceptor``'s can also be `AsyncHandlerInterceptor` in order to receive the
|
||||||
to implement the `afterConcurrentHandlingStarted` callback, which is called
|
`afterConcurrentHandlingStarted` callback on the initial request that starts asynchronous
|
||||||
instead of `postHandle` and `afterCompletion` when asynchronous processing
|
processing instead of `postHandle` and `afterCompletion`.
|
||||||
starts.
|
|
||||||
|
|
||||||
A `HandlerInterceptor` can also register a `CallableProcessingInterceptor`
|
``HandlerInterceptor``'s can also register a `CallableProcessingInterceptor`
|
||||||
or a `DeferredResultProcessingInterceptor` in order to integrate more
|
or a `DeferredResultProcessingInterceptor` in order to integrate more deeply with the
|
||||||
deeply with the lifecycle of an asynchronous request and for example
|
lifecycle of an asynchronous request for example to handle a timeout event. See
|
||||||
handle a timeout event. See the Javadoc of `AsyncHandlerInterceptor`
|
{api-spring-framework}/web/servlet/AsyncHandlerInterceptor.html[AsyncHandlerInterceptor]
|
||||||
for more details.
|
for more details.
|
||||||
|
|
||||||
The `DeferredResult` type also provides methods such as `onTimeout(Runnable)`
|
`DeferredResult` provides `onTimeout(Runnable)` and `onCompletion(Runnable)` callbacks.
|
||||||
and `onCompletion(Runnable)`. See the Javadoc of `DeferredResult` for more
|
See the Javadoc of `DeferredResult` for more details. `Callable` can be substituted for
|
||||||
details.
|
`WebAsyncTask` that exposes additional methods for timeout and completion callbacks.
|
||||||
|
|
||||||
When using a `Callable` you can wrap it with an instance of `WebAsyncTask`
|
|
||||||
which also provides registration methods for timeout and completion.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[mvc-ann-async-http-streaming]]
|
[[mvc-ann-async-http-streaming]]
|
||||||
=== Streaming response
|
=== Streaming response
|
||||||
|
|
||||||
A controller method can use `DeferredResult` and `Callable` to produce its
|
What if you wanted to push multiple events on a single HTTP response? The
|
||||||
return value asynchronously and that can be used to implement techniques such as
|
`ResponseBodyEmitter` return value can be used to stream multiple Objects, where each
|
||||||
http://spring.io/blog/2012/05/08/spring-mvc-3-2-preview-techniques-for-real-time-updates/[long polling]
|
Object sent is serialized with an
|
||||||
where the server can push an event to the client as soon as possible.
|
<<integration.adoc#rest-message-conversion,HttpMessageConverter>> and written to the
|
||||||
|
response. For example:
|
||||||
What if you wanted to push multiple events on a single HTTP response?
|
|
||||||
This is a technique related to "Long Polling" that is known as "HTTP Streaming".
|
|
||||||
Spring MVC makes this possible through the `ResponseBodyEmitter` return value
|
|
||||||
type which can be used to send multiple Objects, instead of one as is normally
|
|
||||||
the case with `@ResponseBody`, where each Object sent is written to the
|
|
||||||
response with an <<integration.adoc#rest-message-conversion,HttpMessageConverter>>.
|
|
||||||
|
|
||||||
Here is an example of that:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0]
|
||||||
[subs="verbatim,quotes"]
|
[subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
@RequestMapping("/events")
|
@GetMapping("/events")
|
||||||
public ResponseBodyEmitter handle() {
|
public ResponseBodyEmitter handle() {
|
||||||
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
|
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
|
||||||
// Save the emitter somewhere..
|
// Save the emitter somewhere..
|
||||||
|
@ -3327,53 +3325,58 @@ Here is an example of that:
|
||||||
emitter.complete();
|
emitter.complete();
|
||||||
----
|
----
|
||||||
|
|
||||||
Note that `ResponseBodyEmitter` can also be used as the body in a
|
`ResponseBodyEmitter` can also be used as the body in a `ResponseEntity` allowing you to
|
||||||
`ResponseEntity` in order to customize the status and headers of
|
customize the status and headers of the response.
|
||||||
the response.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[mvc-ann-async-sse]]
|
[[mvc-ann-async-sse]]
|
||||||
=== Server-Sent Events
|
=== Server-Sent Events
|
||||||
|
|
||||||
`SseEmitter` is a subclass of `ResponseBodyEmitter` providing support for
|
`SseEmitter` is a sub-class of `ResponseBodyEmitter` that provides support for
|
||||||
http://www.w3.org/TR/eventsource/[Server-Sent Events].
|
http://www.w3.org/TR/eventsource/[Server-Sent Events] where events sent from the server
|
||||||
Server-sent events is a just another variation on the same "HTTP Streaming"
|
are formatted according to the W3C SSE specification. In order to produce an SSE
|
||||||
technique except events pushed from the server are formatted according to
|
stream from a controller simply return `SseEmitter`:
|
||||||
the W3C Server-Sent Events specification.
|
|
||||||
|
|
||||||
Server-Sent Events can be used for their intended purpose, that is to push
|
[source,java,indent=0]
|
||||||
events from the server to clients. It is quite easy to do in Spring MVC and
|
[subs="verbatim,quotes"]
|
||||||
requires simply returning a value of type `SseEmitter`.
|
----
|
||||||
|
@GetMapping(path="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||||
|
public SseEmitter handle() {
|
||||||
|
SseEmitter emitter = new SseEmitter();
|
||||||
|
// Save the emitter somewhere..
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
Note however that Internet Explorer does not support Server-Sent Events and
|
// In some other thread
|
||||||
that for more advanced web application messaging scenarios such as online games,
|
emitter.send("Hello once");
|
||||||
collaboration, financial applicatinos, and others it's better to consider
|
|
||||||
Spring's WebSocket support that includes SockJS-style WebSocket emulation
|
// and again later on
|
||||||
falling back to a very wide range of browsers (including Internet Explorer)
|
emitter.send("Hello again");
|
||||||
and also higher-level messaging patterns for interacting with clients through
|
|
||||||
a publish-subscribe model within a more messaging-centric architecture.
|
// and done at some point
|
||||||
For further background on this see
|
emitter.complete();
|
||||||
http://blog.pivotal.io/pivotal/products/websocket-architecture-in-spring-4-0[the following blog post].
|
----
|
||||||
|
|
||||||
|
While SSE is the main option for streaming into browsers, note that Internet Explorer
|
||||||
|
does not support Server-Sent Events. Consider using Spring's
|
||||||
|
<<web.adoc#websocket,WebSocket messaging>> with
|
||||||
|
<<web.adoc#websocket-fallback,SockJS fallback>> transports (including SSE) that target
|
||||||
|
a wide range of browsers.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[mvc-ann-async-output-stream]]
|
[[mvc-ann-async-output-stream]]
|
||||||
=== Streaming raw data
|
=== Streaming raw data
|
||||||
|
|
||||||
`ResponseBodyEmitter` allows sending events by writing Objects to the
|
Sometimes it is useful to bypass message conversion and stream directly to the response
|
||||||
response through an <<integration.adoc#rest-message-conversion,HttpMessageConverter>>.
|
`OutputStream` for example for a file download. Use the of the `StreamingResponseBody`
|
||||||
This is probably the most common case, for example when writing JSON data.
|
return value type to do that:
|
||||||
However sometimes it is useful to bypass message conversion and write directly to the
|
|
||||||
response `OutputStream` for example for a file download. This can be done with the help
|
|
||||||
of the `StreamingResponseBody` return value type.
|
|
||||||
|
|
||||||
Here is an example of that:
|
|
||||||
|
|
||||||
[source,java,indent=0]
|
[source,java,indent=0]
|
||||||
[subs="verbatim,quotes"]
|
[subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
@RequestMapping("/download")
|
@GetMapping("/download")
|
||||||
public StreamingResponseBody handle() {
|
public StreamingResponseBody handle() {
|
||||||
return new StreamingResponseBody() {
|
return new StreamingResponseBody() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -3384,61 +3387,44 @@ Here is an example of that:
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
Note that `StreamingResponseBody` can also be used as the body in a
|
`StreamingResponseBody` can be used as the body in a `ResponseEntity` allowing you to
|
||||||
`ResponseEntity` in order to customize the status and headers of
|
customize the status and headers of the response.
|
||||||
the response.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[mvc-ann-async-reactive-types]]
|
[[mvc-ann-async-reactive-types]]
|
||||||
=== Reactive return values
|
=== Reactive return values
|
||||||
|
|
||||||
If using the reactive `WebClient` from `spring-webflux`, or another client, or
|
Spring MVC supports use of reactive client libraries in a controller. This includes the
|
||||||
a data store with reactive support, you can return reactive types directly from
|
`WebClient` from `spring-webflux` and others such as Spring Data reactive data
|
||||||
Spring MVC controller methods.
|
repositories. In such scenarios it is convenient to be able to return reactive types
|
||||||
|
from the controller method .
|
||||||
|
|
||||||
Spring MVC adapts transparently to the reactive library in use with proper translation
|
Reactive return values are handled as follows:
|
||||||
of cardinality -- i.e. how many values are expected. This is done with the help of the
|
|
||||||
|
* A single-value promise is adapted to and similar to using `DeferredResult`. Examples
|
||||||
|
include `Mono` (Reactor) or `Single` (RxJava).
|
||||||
|
* A multi-value stream, with a streaming media type such as `"application/stream+json"`
|
||||||
|
or `"text/event-stream"`, is adapted to and similar to using `ResponseBodyEmitter` or
|
||||||
|
`SseEmitter`. Examples include `Flux` (Reactor) or `Observable` (RxJava).
|
||||||
|
Applications can also return `Flux<ServerSentEvent>` or `Observable<ServerSentEvent>`.
|
||||||
|
* A multi-value stream, with any other media type (e.g. "application/json"), is adapted
|
||||||
|
to and similar to using `DeferredResult<List<?>>`.
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
====
|
||||||
|
Spring MVC supports Reactor and RxJava through the
|
||||||
{api-spring-framework}/core/ReactiveAdapterRegistry.html[ReactiveAdapterRegistry] from
|
{api-spring-framework}/core/ReactiveAdapterRegistry.html[ReactiveAdapterRegistry] from
|
||||||
`spring-core` which provides pluggable support for reactive and async types. The registry
|
`spring-core` which allows it to adapt from multiple reactive libraries.
|
||||||
has built-in support for RxJava but others can be registered.
|
|
||||||
|
|
||||||
Return values are handled as follows:
|
|
||||||
|
|
||||||
* If the return type has single-value stream semantics such as Reactor `Mono` or
|
|
||||||
RxJava `Single` it is adapted and equivalent to using `DeferredResult`.
|
|
||||||
* If the return type has multi-value stream semantics such as Reactor `Flux` or
|
|
||||||
RxJava `Observable` / `Flowable` and if the media type indicates streaming, e.g.
|
|
||||||
"application/stream+json" or "text/event-stream", it is adapted and equivalent to
|
|
||||||
using `ResponseBodyEmitter` or `SseEmitter`. You can also return
|
|
||||||
`Flux<ServerSentEvent>` or `Observable<ServerSentEvent>`.
|
|
||||||
* If the return type has multi-value stream semantics but the media type does not
|
|
||||||
imply streaming, e.g. "application/json", it is adapted and equivalent to using
|
|
||||||
`DeferredResult<List<?>>`, e.g. JSON array.
|
|
||||||
|
|
||||||
Reactive libraries are detected and adapted to a Reactive Streams `Publisher`
|
|
||||||
through Spring's pluggable `ReactiveAdapterRegistry` which by default supports
|
|
||||||
Reactor 3, RxJava 2, and RxJava 1. Note that for RxJava 1 you will need to add
|
|
||||||
https://github.com/ReactiveX/RxJavaReactiveStreams["io.reactivex:rxjava-reactive-streams"]
|
|
||||||
to the classpath.
|
|
||||||
|
|
||||||
A common assumption with reactive libraries is to not block the processing thread.
|
|
||||||
The `WebClient` with Reactor Netty for example is based on event-loop style
|
|
||||||
handling using a small, fixed number of threads and those must not be blocked
|
|
||||||
when writing to the `ServletResponseOutputStream`. Reactive libraries have
|
|
||||||
operators for that but Spring MVC automatically writes asynchronously so you
|
|
||||||
don't need to use them. The underlying `TaskExecutor` for this must be configured
|
|
||||||
through the MVC Java config and the MVC namespace as described in the following
|
|
||||||
section which by default is a `SyncTaskExecutor` and hence not suitable for
|
|
||||||
production use.
|
|
||||||
|
|
||||||
[NOTE]
|
|
||||||
====
|
|
||||||
Unlike Spring MVC, Spring WebFlux is built on a non-blocking, reactive foundation
|
|
||||||
and uses the Servlet 3.1 non-blocking I/O that's also based on event loop style
|
|
||||||
processing and hence does not require a thread to absorb the effect of blocking.
|
|
||||||
====
|
====
|
||||||
|
|
||||||
|
When streaming to the response with a reactive type, Spring MVC performs (blocking)
|
||||||
|
writes to the response through the
|
||||||
|
through the <<mvc-ann-async-configuration-spring-mvc,configured>> MVC `TaskExecutor`.
|
||||||
|
By default this is a `SyncTaskExecutor` and not suitable for production.
|
||||||
|
https://jira.spring.io/browse/SPR-16203[SPR-16203] will provide better defaults.
|
||||||
|
In the mean time please configure the executor through the MVC config.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[mvc-ann-async-configuration]]
|
[[mvc-ann-async-configuration]]
|
||||||
|
|
Loading…
Reference in New Issue