Spring mvc 异步调用 Callable DeferredResult

Spring MVC 3.2开始引入了基于Servlet3的异步请求处理,相比以前,控制器方法已经不一定需要一个值,而是可以直接返回一个Callable对象,并通过Spring MVC所管理的线程来产生返回值,与此同时,Servlet容器的主线程则可以退出并释放其资源,同时也允许容器去处理其它请求。通过一个TaskExecutor,Spring MVC可以在另外的线程中调用Callable。当Callable返回时,请求在携带Callable返回的值,再次被分配到Servlet容器中恢复处理流程。如实例 异步调用方式一

另一个选择是让控制器返回一个DeferredResult的实例。例如 异步调用方式二

实例:

@RestController
public class TestController {

    /**
     * 异步调用 方式一,直接返回Callable
     * 127.0.0.1:8886/get3
     *
     * @return
     */
    @GetMapping(value = "/get3")
    public Callable get3() {
        return () -> "someView";
    }


    /**
     * 异步调用 方式二,返回一个DeferredResult
     * 127.0.0.1:8886/get4  先调用此方法,会等待
     * 在调用
     * 127.0.0.1:8886/get5 会返回 hello world
     * 另一个选择是,让控制器方法返回一个DeferredResult的实例,在这种场景下,
     * 返回值可以由任何一个线程产生,包括哪些不是由Spring MVC管理的线程,
     * 举个例子,返回值可以是为了响应某些外部事件所产生,比如JMS消息,计划任务等等
     *
     * @return
     */
    private DeferredResult deferredResult;

    @GetMapping(value = "/get4")
    public DeferredResult get4() {
        DeferredResult deferredResult = new DeferredResult<>();
        this.deferredResult = deferredResult;
        return deferredResult;
    }

    @GetMapping(value = "/get5")
    public void get5() {
        if (Objects.nonNull(deferredResult)) {
            deferredResult.setResult("hello world");
        }
    }
}

Servlet 3.0异步请求运作机制的部分原理

a.Servlet请求ServletRequest可以通过调用request.startAsync()方法而进入异步模式,这样做的主要结果就是该Servlet以及所有的过滤器都可以结束但其相应(Response)会留待异步处理结束后在返回调用。

b.request.startAsync()方法会返回一个AsyncContext对象,可以用他对异步处理进行进一步的控制和操作,比如说他也提供了一个与反转(forward)很相似的dispatch方法,只不过他允许应用恢复Servlet容器的请求处理进程。

c.ServletRequest提供了获取当前DispatherType的方式,后者可以用来区别当前处理的是原始请求,异步分发请求,转向或者是其它类型的请求分发类型。

下面再来看看Callable的异步请求被处理时所发生的事件

a.Spring  MVC 开始执行异步处理,并且把该Callable对象提交给另一个独立线程的执行器TaskExecutor处理。

b.DispatcherServlet和所有过滤器都退出Servlet容器线程,但此时方法的响应对象仍未返回。

c.Callable对象最终产生一个返回结果,此时Spring MVC会重新把请求分派回Servlet容器,恢复处理。

d.DispatcherServlet再次被调用,恢复对Callable异步处理所返回结果的处理


对 DeferredResult对象请求的处理顺序也非常类似,区别在于应用可以通过任何线程来计算返回一个结果

a.控制器先返回一个DeferredResult对象,并把它存取在内存(队列或者列表等)中以便存取

b.Spring MVC开始异步处理

c.DispatcherServlet和所有过滤器都退出Servlet容器线程,但此时方法的响应对象仍未返回。

d.由处理该请求的线程对DeferredResult进行设值,然后Spring MVC会重新把 请求分派回Serlvet容器恢复处理

e.DispatcherServlet再次被调用,恢复对该异步返回结果处理。


异步结果的异常处理:

如果Callable在执行过程中抛出异常 与一般的控制器异常一样,会被正常的异常处理流程捕获处理

如果返回方法是一个DeferredResult对象,可以选择

deferredResult.setErrorResult()


拦截异步请求

处理连接器HandlerInterceptor可以实现AsyncHandlerInterceptor接口拦截异步请求,因为在异步请求的开始时,被调用的回调方法是该接口的afterConcurrentHandlingStarted方法,而不是一般的postHandle 和 afterCompletion方法。如果需要与异步请求处理的生命流程有更深入的集成,比如需要处理timeout的事件等,则HandlerInterceptor需要注册CallableProcessingInterceptor或DeferredResultProcessingInterceptor拦截器,更多细节需要参考AsyncHandlerInterceptor类的Java文档。

DeferredResult类还提供了onTimeout(Runnable)和onCompletion(Runnable)等方法可以参考DeferredResult的java文档

Callable需要请求过期(timeout)和完成后的拦截时,可以把他包装在一个WebAsyncTask实例中,后者提供了相关技术支持。






你可能感兴趣的:(Spring文档学习)