您现在的位置是:首页 >技术杂谈 >Spring:spring-web中DeferredResult执行过程分析网站首页技术杂谈

Spring:spring-web中DeferredResult执行过程分析

netyeaxi 2024-07-04 11:17:55
简介Spring:spring-web中DeferredResult执行过程分析

       对于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

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。