对于HTTP请求的处理,有时处理请求的时间较长,可能会采用异步处理方式来处理。一般常用的异步处理方式是采用DeferredResult,本文会简单分析一下spring-web的整个处理过程。
首先,提供一个简单的DeferredResult例子:
@RestController
public class TestController {
@PostMapping("/test/doRequest")
public DeferredResult<String> doRequest(@RequestBody(required = false) String body, HttpServletRequest request) {
System.out.println("======================" + request.getDispatcherType());
String ret = "ok:" + System.currentTimeMillis();
DeferredResult<String> dr = new DeferredResult<String>(10000L);
Executors.newSingleThreadExecutor().execute(new Runnable() {
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {}
dr.setResult(ret);
}
});
return dr;
}
}
DeferredResult的内部实际上是使用servlet3.0规范中异步servlet实现的。
以下对版本spring-web-5.2.10.Release的代码进行分析。
HTTP主要是由org.springframework.web.servlet.DispatcherServlet的doDispatch(...)处理的,其中的主要处理方法如下:
(1)、(4)会分别调用org.springframework.web.servlet.HandlerInterceptor中的对应方法:
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
(6)会调用org.springframework.web.servlet.AsyncHandlerInterceptor中的对应方法:
public interface AsyncHandlerInterceptor extends HandlerInterceptor {
default void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
}
}
从(2)进入后,会调用到org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod的如下方法:
(7)会调用到TestController.doRequest方法,返回的returnValue就是DeferredResult对象。
(8)中会调用org.springframework.web.servlet.mvc.method.annotation.DeferredResultMethodReturnValueHandler的如下方法:
其中最后一行调用的是org.springframework.web.context.request.async.WebAsyncManager如下方法:
(9)会调用如下方法
其中483行会实际调用javax.servlet.ServletRequest.startAsync(ServletRequest servletRequest,ServletResponse servletResponse)的一个实现,从而开启一个异步处理流程。
(10)调用的是org.springframework.web.context.request.async.DeferredResult如下方法:
这里需要特别关注(11),它分2种情况:
#1.如果DeferredResult.setResult(...)在此之前未被调用过,则直接返回
#2.如果DeferredResult.setResult(...)在此之前已被调用过,则执行(12)
其中(12)会调用(13),(13)会调用(14),(14)实际会调用javax.servlet.ServletRequest.dispatch()的实现。若此方法被调用,实际会在servlet容器内部再发起一个针对/test/doRequest的请求。
然后返回到(2)后继续向下执行到(3),(3)最终调用的是org.springframework.web.context.request.async.StandardServletAsyncWebRequest如下方法:
本质上是调用javax.servlet.ServletRequest.isAsyncStarted()的实现。
此时(3)的值为false,程序直接返回。
这里有一个可疑的地方,如果TestController的代码为:
@RestController
public class TestController {
@PostMapping("/test/doRequest")
public DeferredResult<String> doRequest(@RequestBody(required = false) String body, HttpServletRequest request) {
System.out.println("======================" + request.getDispatcherType());
String ret = "ok:" + System.currentTimeMillis();
DeferredResult<String> dr = new DeferredResult<String>(10000L);
dr.setResult(ret);
return dr;
}
}
即测试代码中如上直接给DeferredResult设置值,执行javax.servlet.ServletRequest.dispatch()后,根据servlet3.0规范,此时javax.servlet.ServletRequest.isAsyncStarted()应该返回false。但实际用tomcat、jetty都试过,都是返回true,整个流程执行的没有问题。可放到webloigc中执行时,则是返回false,执行整个流程的会报错。
无论是#1还是#2,最终都会调用到(13),从而在servlet容器内部再发起一个针对/test/doRequest的请求,此时程序会再次执行到(2),然后会在org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter的(15)中获取到DeferredResult中设置的值
最终由org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcesso的handleReturnValue方法将返回写回给客户端。
参考文档
The Java EE Tutorial:17.12 Asynchronous Processing
spring-framework-docs:1.6. Asynchronous Requests