Summary of Spring Web MVC knowledge points (2) - official original version

1. Asynchronous request

Spring MVC has extensive integration with Servlet asynchronous request handling:

  • DeferredResult and Callable return values ​​in controller methods provide basic support for a single asynchronous return value.
  • The controller can stream multiple values, including SSE and raw data.
  • A controller can use a reactive client and return a reactive type to handle the response.

1. DeferredResult

Once asynchronous request handling is enabled in the servlet container, the controller method can wrap  DeferredResult the return value of any supported controller method, as shown in the following example:

@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
    DeferredResult<String> deferredResult = new DeferredResult<>();
    // Save the deferredResult somewhere..
    return deferredResult;
}

// From some other thread...
deferredResult.setResult(result);

Controllers can produce return values ​​asynchronously from different threads—for example, in response to external events (JMS messages), scheduled tasks, or other events.

2. Callable

A controller can java.util.concurrent.Callable be used to wrap any supported return value, as the following example shows:

@PostMapping
public Callable<String> processUpload(final MultipartFile file) {
    return () -> "someView";
}

The return value can then be obtained by running the given task through the configured TaskExecutor.

3. Processing

Here is a very succinct overview of Servlet asynchronous request handling:

  • ServletRequestrequest.startAsync() Asynchronous mode  can be entered by calling  . The main effect of this is that the servlet (and any filters) can exit, but the response is still open for processing to complete later.

  • The right  request.startAsync() call returns  AsyncContext, which you can use to further control the asynchronous processing. For example, it provides  dispatch methods that are similar to forward in the Servlet API, except that it allows the application to resume request processing on the Servlet container thread.

  • ServletRequest Provides  DispatcherType access to the current, which you can use to differentiate between processing the initial request, asynchronous dispatch, forwarding, and other dispatcher types.

DeferredResult The processing works as follows:

  • The controller returns  DeferredResult, and saves it in some in-memory queue or list that can be accessed.

  • Spring MVC calls  request.startAsync().

  • At the same time, DispatcherServlet the request processing thread exits with all configured filters, but the response remains open.

  • The application is set up from some thread  DeferredResult, and Spring MVC dispatches the request back to the Servlet container.

  • DispatcherServlet is called again, and processing resumes with the asynchronously produced return value.

Callable Processing works like this:

  • The controller returns one  Callable.

  • Spring MVC calls  request.startAsync(), and will  Callable submit to  TaskExecutor, handle them in a separate thread.

  • At the same time, DispatcherServlet all filters exit the Servlet container thread, but the response is still open.

  • Eventually, Callable a result is produced and Spring MVC dispatches the request back to the Servlet container to complete processing.

  • DispatcherServlet is called again, and  Callable processing continues with the asynchronously produced return value from .

exception handling

When you use  DeferredResult , you can choose whether to call  setResult or  setErrorResult not with an exception. In both cases, Spring MVC dispatches the request back to the Servlet container to complete processing. It is then handled as if the controller method returned the given value or raised the given exception. The exception then goes through the normal exception handling mechanisms (e.g. calling  @ExceptionHandler a method).

Callable Similar processing logic occurs when  you use  , the main difference is whether the result is Callable returned, or it raises an exception

 to intercept

HandlerInterceptor instances can be of type AsyncHandlerInterceptor to receive the afterConcurrentHandlingStarted callback (instead of postHandle and afterCompletion ) on the initial request to start asynchronous processing.
Implementations of HandlerInterceptor can also register a CallableProcessingInterceptor or DeferredResultProcessingInterceptor to integrate more deeply with the life cycle of an asynchronous request (for example, to handle a timeout event) DeferredResult
provides onTimeout(Runnable) and onCompletion(Runnable) callbacks

Asynchronous Spring MVC vs. WebFlux
The Servlet API was originally built for a single pass in the Filter-Servlet chain. Asynchronous request processing lets the application exit the Filter-Servlet chain, but leaves the response for further processing. Spring MVC's asynchronous support is built around this mechanism. When the controller returns a DeferredResult, the Filter-Servlet chain is exited and the Servlet container thread is released. Later, when the DeferredResult is set, an ASYNC dispatch (to the same URL), during which the controller is mapped again, but instead of calling it, the DeferredResult value is used (as if the controller returned it), to resume processing.
In contrast, Spring WebFlux is neither built on the Servlet API, nor does it require such asynchronous request handling capabilities, since it is asynchronous by design. Asynchronous processing is built into all framework contracts and is inherently supported through all phases of request processing.

From a programming model perspective, both Spring MVC and Spring WebFlux support asynchronous and reactive (Reactive) types as return values ​​of controller methods. Spring MVC even supports streams, including reactive backpressure. However, individual writes to the response are still blocking (and executed on a separate thread), unlike WebFlux, which relies on non-blocking I/O and does not require additional threads for each write.
Another fundamental difference is that Spring MVC does not support asynchronous or reactive types in controller method parameters (for example, @RequestBody, @RequestPart, etc.), nor does it explicitly support asynchronous and reactive types as model attributes. Spring WebFlux does support all of them.
Finally, from a configuration standpoint, asynchronous request handling must be enabled at the Servlet container level.

二、 HTTP Streaming

You can use DeferredResult and Callable to implement a single asynchronous return value. What if you want to generate multiple asynchronous values ​​and have them written to the response? This section describes how to do this.
1.Objects
You can use the return value of ResponseBodyEmitter to generate a stream of objects, each of which is serialized by HttpMessageConverter and written to the response, as shown in the following example:

@GetMapping("/events")
public ResponseBodyEmitter handle() {
    ResponseBodyEmitter emitter = new ResponseBodyEmitter();
    // Save the emitter somewhere..
    return emitter;
}

// In some other thread
emitter.send("Hello once");

// and again later on
emitter.send("Hello again");

// and done at some point
emitter.complete();

You can also use ResponseBodyEmitter as the body in ResponseEntity, allowing you to customize the status and header of the response.
When the emitter throws an IOException (for example, if the remote client disappears), the application is not responsible for cleaning up the connection, nor should it call emitter.complete or emitter.completeWithError. Instead, the servlet container automatically initiates an AsyncListener error notification in which Spring MVC makes a completeWithError call. This call in turn performs the final ASYNC dispatch to the application, during which Spring MVC invokes the configured exception resolver and completes the request.

2.SSE

SseEmitter (a subclass of ResponseBodyEmitter) provides Server-Sent Events support, and events sent from the server are formatted according to the W3C SSE specification. To generate an SSE stream from a controller, return SseEmitter, as shown in the following example:

@GetMapping(path="/events", produces=MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter handle() {
    SseEmitter emitter = new SseEmitter();
    // Save the emitter somewhere..
    return emitter;
}

// In some other thread
emitter.send("Hello once");

// and again later on
emitter.send("Hello again");

// and done at some point
emitter.complete();

While SSE streams are the primary choice for browsers, please note that Internet Explorer does not support Server-Sent Events. Consider using Spring's WebSocket messaging with SockJS fallback transports (including SSE), which targets a wide range of browsers.

3. Raw Data

Sometimes  OutputStream it is useful to bypass message conversion and flow directly to the response (for example, for file downloads). You can  StreamingResponseBody do this using return types, as the following example shows:

@GetMapping("/download")
public StreamingResponseBody handle() {
    return new StreamingResponseBody() {
        @Override
        public void writeTo(OutputStream outputStream) throws IOException {
            // write...
        }
    };
}

You can  ResponseEntity use  StreamingResponseBody it as the body to customize the status and header information of the response.

Guess you like

Origin blog.csdn.net/leesinbad/article/details/130042140