用法
@GetMapping("/ddd")
public Callable<String> process() {
return () -> {
Thread.sleep(1000L);
return "call";
};
}
@GetMapping("/ddd")
public DeferredResult<String> quotes() {
DeferredResult<String> deferredResult = new DeferredResult<String>();
return deferredResult;
}
// In some other thread...
deferredResult.setResult(data);
返回Callable和DeferredResult区别在于the return value of DeferredResult will also be produced from any thread, i.e. one that is not managed by Spring MVC.
运行时
- 客户端请求,DispatcherType:REQUEST 即与同步请求一样
- Controller返回Callable
- HandlerMethodReturnValueHandlerComposite选出CallableMethodReturnValueHandler来处理Callable
- WebAsyncManager#startCallableProcessing -> #startAsyncProcessing -> StandardServletAsyncWebRequest#startAsync
- 由于spring的StandardServletAsyncWebRequest内部托管J2EE容器的Request(该Request实现ServletRequest),所以调用ServletRequest#startAsync(ServletRequest, ServletResponse)生成AsyncContext托管于StandardServletAsyncWebRequest内部,该AsyncContext保存ServletRequest和ServletResponse,以便之后的DispatcherType:ASYNC请求能取出对应的ServletResponse
- WebAsyncManager#startCallableProcessing 调用完毕后,向taskExecutor线程池中提交任务来执行Callable#call,注意该任务最后必定会执行WebAsyncManager#setConcurrentResultAndDispatch,即执行StandardServletAsyncWebRequest#dispatch,就是执行前面生成的AsyncContext#dispatch
- DispatcherType:ASYNC 这是由AsyncContext#dispatch发起的。
- 注意这时RequestMappingHandlerAdapter#invokeHandlerMethod(HttpServletRequest, HttpServletResponse, HandlerMethod)中if (asyncManager.hasConcurrentResult())结果为true,即将原来的ServletInvocableHandlerMethod替换成ConcurrentResultHandlerMethod,所以invocableMethod.invokeAndHandle(webRequest, mavContainer);直接返回taskExecutor线程池执行的结果。
- HandlerMethodReturnValueHandlerComposite选出你结果类型对应的ReturnValueHandler来处理你的结果。
- 返回客户端。
提醒:RequestMappingHandlerAdapter#invokeHandlerMethod(HttpServletRequest, HttpServletResponse, HandlerMethod)中的invocableMethod.invokeAndHandle(webRequest, mavContainer);是你业务代码执行的步骤。
The call to request.startAsync() returns AsyncContext which can be used for further control over async processing. For example it provides the method dispatch, that is similar to a forward from the Servlet API except it allows an application to resume request processing on a Servlet container thread.
运行时流程有点绕,没功夫画时序图。
Exception Handling
when a Callable raises an Exception Spring MVC dispatches to the Servlet container with the Exception as the result and that leads to resume request processing with the Exception instead of a controller method return value. When using a DeferredResult you have a choice whether to call setResult or setErrorResult with an Exception instance.
所以异步异常处理和同步相同,在DispatcherType:ASYNC这段请求中处理。
Intercepting Async Requests
- The DeferredResult type also provides methods such as onTimeout(Runnable) and onCompletion(Runnable).
- When using a Callable you can wrap it with an instance of WebAsyncTask which also provides registration methods for timeout and completion.
AsyncHandlerInterceptor比HandlerInterceptor多#afterConcurrentHandlingStarted,调用时间即DispatcherType:ASYNC发起之前。
HTTP Streaming
正是请求被异步化,从而使得能long polling,即HTTP Streaming。
可以用ResponseBodyEmitter来创建逻辑上的Streaming,注意ResponseBodyEmitter#send的内容都会通过对应的HttpMessageConverter来转化:
@RequestMapping("/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();
当然 ResponseBodyEmitter can also be used as the body in a ResponseEntity in order to customize the status and headers of the response.