Spring MVC异步处理简介

Spring MVC异步处理简介

Github地址

相关系列文章:

本文讲到的所有特性皆是基于Servlet 3.0 Async Processing的,不是基于Servlet 3.1 Async IO的。

Callable<?>

A Callable<?> can be returned when the application wants to produce the return value asynchronously in a thread managed by Spring MVC.

用于异步返回结果,使用的是Spring MVC的AsyncTaskExecutor,Spring MVC使用CallableMethodReturnValueHandler负责处理它。

下面是例子CallableController

@RestController
public class CallableController {

  @RequestMapping("callable-hello")
  public Callable<String> hello() {
    return () -> new SlowJob("CallableController").doWork();
  }
}

用浏览器访问:http://localhost:8080/callable-hello 查看返回结果。

DeferredResult<?>

A DeferredResult<?> can be returned when the application wants to produce the return value from a thread of its own choosing.

用于异步返回结果,使用的是client code自己的thread,Spring MVC使用DeferredResultMethodReturnValueHandler负责处理它。

下面是例子DeferredResultController

@RestController
public class DeferredResultController {

  @Autowired
  @Qualifier("customExecutorService")
  private ExecutorService executorService;

  @RequestMapping("deferred-result-hello")
  public DeferredResult<String> hello() {
    DeferredResult<String> deferredResult = new DeferredResult<>();
    executorService.submit(() -> {
      try {
        deferredResult.setResult(new SlowJob("DeferredResultController").doWork());
      } catch (Exception e) {
        deferredResult.setErrorResult(e);
      }

    });
    return deferredResult;
  }

}

在这个例子里使用了ExecutorService(见ExecutorServiceConfiguration),你也可以根据实际情况采用别的机制来给DeferredResult.setResult

用浏览器访问:http://localhost:8080/deferred-result-hello 查看返回结果。

ListenableFuture<?> or CompletableFuture<?>/CompletionStage<?>

A ListenableFuture<?> or CompletableFuture<?>/CompletionStage<?> can be returned when the application wants to produce the value from a thread pool submission.

用于异步返回结果,使用client code自己的thread pool,Spring MVC使用DeferredResultMethodReturnValueHandler负责处理它。

下面是例子ListenableFutureController

@RestController
public class ListenableFutureController {

  @Autowired
  @Qualifier("customExecutorService")
  private ExecutorService executorService;

  @RequestMapping("listenable-future-hello")
  public ListenableFutureTask<String> hello() {

    ListenableFutureTask<String> listenableFutureTask = new ListenableFutureTask<>(
        () -> new SlowJob("ListenableFutureController").doWork());
    executorService.submit(listenableFutureTask);
    return listenableFutureTask;
  }

}

用浏览器访问:http://localhost:8080/listenable-future-hello 查看返回结果。

下面是例子CompletionFutureController

@RestController
public class CompletionFutureController {

  @RequestMapping("completable-future-hello")
  public CompletableFuture<String> hello() {

    return CompletableFuture
        .supplyAsync(() -> new SlowJob("CompletionFutureController").doWork());
  }

}

用浏览器访问:http://localhost:8080/completable-future-hello 查看返回结果。

ResponseBodyEmitter

A ResponseBodyEmitter can be returned to write multiple objects to the response asynchronously; also supported as the body within a ResponseEntity.

用于异步的写入多个消息,使用的是client code自己的thread,Spring MVC使用ResponseBodyEmitterReturnValueHandler负责处理它。

下面是例子ResponseBodyEmitterController

@RestController
public class ResponseBodyEmitterController {

  @Autowired
  @Qualifier("customExecutorService")
  private ExecutorService executorService;

  @RequestMapping("response-body-emitter-hello")
  public ResponseBodyEmitter hello() {

    ResponseBodyEmitter emitter = new ResponseBodyEmitter();
    executorService.submit(() -> {
      try {
        for (int i = 0; i < 5; i++) {

          String hello = new SlowJob("ResponseBodyEmitterController").doWork();
          emitter.send("Count: " + (i + 1));
          emitter.send("\n");
          emitter.send(hello);
          emitter.send("\n\n");
        }
        emitter.complete();
      } catch (Exception e) {
        emitter.completeWithError(e);
      }

    });

    return emitter;
  }
}

用浏览器访问:http://localhost:8080/response-body-emitter-hello 查看返回结果。

SseEmitter

An SseEmitter can be returned to write Server-Sent Events to the response asynchronously; also supported as the body within a ResponseEntity.

作用和ResponseBodyEmitter类似,也是异步的写入多个消息,使用的是client code自己的thread,区别在于它使用的是Server-Sent Events。Spring MVC使用ResponseBodyEmitterReturnValueHandler负责处理它。

下面是例子SseEmitterController

@RestController
public class SseEmitterController {

  @Autowired
  @Qualifier("customExecutorService")
  private ExecutorService executorService;

  @RequestMapping("sse-emitter-hello")
  public ResponseBodyEmitter hello() {

    SseEmitter emitter = new SseEmitter();
    executorService.submit(() -> {
      try {
        for (int i = 0; i < 5; i++) {

          String hello = new SlowJob("SseEmitterController").doWork();
          StringBuilder sb = new StringBuilder();
          sb.append("Count: " + (i + 1)).append(". ").append(hello.replace("\n", ""));
          emitter.send(sb.toString());
        }
        emitter.complete();
      } catch (Exception e) {
        emitter.completeWithError(e);
      }

    });

    return emitter;
  }
}

用浏览器访问:http://localhost:8080/sse-emitter-hello 查看返回结果。

StreamingResponseBody

A StreamingResponseBody can be returned to write to the response OutputStream asynchronously; also supported as the body within a ResponseEntity.

用于异步write outputStream,使用的是Spring MVC的AsyncTaskExecutor,Spring MVC使用StreamingResponseBodyReturnValueHandler负责处理它。要注意,Spring MVC并没有使用Servlet 3.1 Async IO([Read|Write]Listener)。

下面是例子StreamingResponseBodyController

@RestController
public class StreamingResponseBodyController {

  @RequestMapping("streaming-response-body-hello")
  public StreamingResponseBody hello() {

    return outputStream -> {
      String hello = new SlowJob("CallableController").doWork();
      outputStream.write(hello.getBytes());
      outputStream.flush();
    };

  }
}

用浏览器访问:http://localhost:8080/streaming-response-body-hello 查看返回结果。

配置MVC Async

AsyncTaskExecutor

Spring MVC执行异步操作需要用到AsyncTaskExecutor,这个可以在用WebMvcConfigurer.configureAsyncSupport方法来提供(相关文档)。 如果不提供,则使用SimpleAsyncTaskExecutorSimpleAsyncTaskExecutor不使用thread pool,因此推荐提供自定义的AsyncTaskExecutor

需要注意的是@EnableAsync也需要用到AsyncTaskExecutor,不过Spring MVC和它用的不是同一个。 顺带一提,EnableAsync默认也使用SimpleAsyncTaskExecutor,可以使用AsyncConfigurer.getAsyncExecutor方法来提供一个自定义的AsyncTaskExecutor

例子见:MvcAsyncTaskExecutorConfigurer

Interceptors

  • AsyncHandlerInterceptor,使用WebMvcConfigurer.addInterceptors注册
  • CallableProcessingInterceptor[Adapter],使用WebMvcConfigurer.configureAsyncSupport注册
  • DeferredResultProcessingInterceptor[Adapter],使用WebMvcConfigurer.configureAsyncSupport注册

官方文档:Intercepting Async Requests

WebAsyncManager

WebAsyncManager是Spring MVC管理async processing的中心类,如果你可以阅读它的源码来更多了解Spring MVC对于async processing的底层机制。

参考资料

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏中国白客联盟

Evil Package

那如果我们安装packages是恶意的怎么办呢,我们根据手册编写如下恶意setup.py

952
来自专栏后台及大数据开发

CentOS下 elasticsearch集群安装

4.修改/root/elasticsearch-node3/config/elasticsearch.yml 为如下内容(注意红色部分为三个节点不一致的地方)

1112
来自专栏Ryan Miao

Spring Boot文档阅读

原因之初 最初习惯百度各种博客教程,然后跟着操作,因为觉得跟着别人走过的路走可以少走很多弯路,省时间。然而,很多博客的内容并不够完整,甚至错误,看多了的博客甚至...

5737
来自专栏玩转JavaEE

Spring Cloud中声明式服务调用Feign

前面几篇文章我们详细的介绍了Ribbon、RestTemplate、Hystrix组件,这些组件是我们Spring Cloud中非常基础的组件,小伙伴们在使用的...

77211
来自专栏平凡文摘

Spring Boot 内嵌容器 Tomcat / Undertow / Jetty 优雅停机实现

3782
来自专栏一个会写诗的程序员的博客

《Springboot极简教程》继承WebMvcConfigurerAdapter: 一行代码写Controller文章概要常用的写Controller类方法继承 WebMvcConfigurerAd

要添加一个新页面访问总是要新增一个Controller或者在已有的一个Controller中新增一个方法,然后再跳转到设置的页面上去。考虑到大部分应用场景中Vi...

651
来自专栏JAVA后端开发

spring boot2集成activiti6的问题记录

经查,是因为我用mybatis plus,要求用mybatis3.4.6,而activiti用的是mybatis3.4.2,两边有冲突,直接排除activiti...

1.1K3
来自专栏名山丶深处

springboot集成redis(mybatis、分布式session)

2018
来自专栏Java成神之路

SpringBoot_Exception_02_Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:1.5

1153
来自专栏张善友的专栏

Windows Server AppFabric Beta 2 已经发布

Windows Server AppFabric Beta 2是一个包含完全功能的AppFabric版本(This build represents our “...

1815

扫码关注云+社区

领取腾讯云代金券