Servlet异步处理
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("doget");
method3(request,response);
}
/**
* 使用asyncContext执行异步请求
* @param request
* @param response
*/
public void method1(HttpServletRequest request, HttpServletResponse response) {
//获取异步上下文
AsyncContext asyncContext = request.startAsync();
//开启异步处理过程
asyncContext.start(() -> {
//耗时处理
try {
Thread.sleep(2000);
//通过AsyncContext返回 respsonse
asyncContext.getResponse().getWriter().write("Hello World1!");
} catch (Exception e) {
e.printStackTrace();
}
//异步处理完成
asyncContext.complete();
});
}
/**
* 使用自身线程池来执行异步请求
* @param request
* @param response
*/
public void method2(HttpServletRequest request, HttpServletResponse response) {
AsyncContext asyncContext = request.startAsync();
Runnable runnable = () -> {
try {
Thread.sleep(2000);
asyncContext.getResponse().getWriter().write("Hello World2!");
} catch (Exception e) {
e.printStackTrace();
}
asyncContext.complete();
};
new Thread(runnable).start();
}
/**
* 使用asyncContext.dispath()来重新提交请求
* @param request
* @param response
*/
public void method3(HttpServletRequest request, HttpServletResponse response) {
Object result = request.getAttribute("result");
if(result==null) {
AsyncContext asyncContext = request.startAsync();
Runnable runnable = () -> {
try {
Thread.sleep(2000);
//
request.setAttribute("result", "Hello World3!");
} catch (Exception e) {
e.printStackTrace();
}
asyncContext.dispatch();
};
new Thread(runnable).start();
}else{
try {
response.getWriter().write(result.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
复制代码
上面列了AsyncContext使用的三种方式:
-
commit(): 标识异步事件的结束,http通道关闭
- 使用AsyncConext线程池执行任务
- 使用业务线程池执行任务
-
dispath(): 通知servlet容器,重新发起请求(doService()会收到两次请求)
异步处理过程
从序列图上可以看到SpringMVC在处理异步请求时,DispatcherServlet会处理两次请求
具体来看HandlerAdapter的处理过程
//根据HandlerMethod解析参数 并完成过程调用得到一个ModelAndView
private ModelAndView invokeHandleMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
//对@InitBinder的处理 主要是聚合了@InitBinder的所有处理方法
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
//@ModelAttribute的处理
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
//对HandlerMethod的装饰,主要是增加了参数解析和返回值转化的功能
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//提供对参数解析的支持
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
//提供对返回值解析的支持
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
//提供对@InitBinder处理的支持
invocableMethod.setDataBinderFactory(binderFactory);
//TODO 尚不明功能
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
//可以看做handler()过程的上下文
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);
==========================异步处理分割线=============
//AsyncWebRequest内部持有AsyncContext 可以通过其开启异步任务
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
//异步处理Manager
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
//设置异步执行线程池
asyncManager.setTaskExecutor(this.taskExecutor);
//提供对异步处理的支持
asyncManager.setAsyncWebRequest(asyncWebRequest);
//异步调用拦截器
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
//如果异步处理完成
if (asyncManager.hasConcurrentResult()) {
//获取异步执行结果
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
...
//替换invocableMethod(原先异步处理的方法返回值是Callable现在直接返回结果)
invocableMethod = invocableMethod.wrapConcurrentResult(result);
}
//对invocableMethod进行参数解析,过程调用,返回值转化
//并将结果存到mavContainer中
invocableMethod.invokeAndHandle(webRequest, mavContainer);
//如果异步处理正在执行(已经开始,尚未结束) 立刻返回
//同时DispatcherServlet也直接返回 等待AsyncContext.dispatch()调用再次进入doDispatch()方法
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
//从mavContainer捞出结果
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
复制代码
这里异步的处理 针对两次请求有两种处理
-
第一次请求: 开始异步请求
//AsyncWebRequest内部持有AsyncContext 可以通过其开启异步任务 AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response); asyncWebRequest.setTimeout(this.asyncRequestTimeout); //异步处理Manager WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); //设置异步执行线程池 asyncManager.setTaskExecutor(this.taskExecutor); //提供对异步处理的支持 asyncManager.setAsyncWebRequest(asyncWebRequest); //异步调用拦截器 asyncManager.registerCallableInterceptors(this.callableInterceptors); asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors); //对invocableMethod进行参数解析,过程调用(调用AsyncWebRequest.startAsync()执行异步过程),返回值转化 //并将结果存到mavContainer中 invocableMethod.invokeAndHandle(webRequest, mavContainer); //如果异步处理正在执行(已经开始,尚未结束) 立刻返回 //同时DispatcherServlet也直接返回 return null; #org.springframework.web.servlet.DispatcherServlet protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ... mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); if (asyncManager.isConcurrentHandlingStarted()) { return; } ... } //等待AsyncWebRequest.dispatch()被调用 然后再次进入doDispatch()方法 复制代码
其实可以看到 在invokeHandleMethod()的处理过程除了最后直接返回null,前面的处理都和正常流程是一样的
在SpringMVC中异步方法无非只是返回值是个Callable()而已 ,所以其参数解析过程和正常流程是一样的,区别在于返回值解析过程
来看返回值处理过程
public class CallableMethodReturnValueHandler implements AsyncHandlerMethodReturnValueHandler { @Override public boolean supportsReturnType(MethodParameter returnType) { return Callable.class.isAssignableFrom(returnType.getParameterType()); } @Override public boolean isAsyncReturnValue(Object returnValue, MethodParameter returnType) { return (returnValue != null && returnValue instanceof Callable); } @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { if (returnValue == null) { mavContainer.setRequestHandled(true); return; } //将Callable对象丢给异步执行器执行 Callable<?> callable = (Callable<?>) returnValue; WebAsyncUtils.getAsyncManager(webRequest).startCallableProcessing(callable, mavContainer); } } #org.springframework.web.context.request.async.WebAsyncManager public void startCallableProcessing(final WebAsyncTask<?> webAsyncTask, Object... processingContext) throws Exception { //超时控制,拦截器配置 ... //调用Request.startAsync()得到AsyncContext对象 startAsyncProcessing(processingContext); try { this.taskExecutor.submit(new Runnable() { @Override public void run() { Object result = null; try { interceptorChain.applyPreProcess(asyncWebRequest, callable); result = callable.call(); } ... finally { result = interceptorChain.applyPostProcess(asyncWebRequest, callable, result); } //通知异步执行结束 调用dispatch() setConcurrentResultAndDispatch(result); } }); } catch (RejectedExecutionException ex) { //异常处理 Object result = interceptorChain.applyPostProcess(this.asyncWebRequest, callable, ex); setConcurrentResultAndDispatch(result); throw ex; } } private void setConcurrentResultAndDispatch(Object result) { ... //调用AsyncContext.dispatch() 通知servlet容器再起发起请求 this.asyncWebRequest.dispatch(); } 复制代码
2. 第二次请求: 异步执行完成
```java
//AsyncWebRequest内部持有AsyncContext 可以通过其开启异步任务
AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
asyncWebRequest.setTimeout(this.asyncRequestTimeout);
//异步处理Manager
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
//设置异步执行线程池
asyncManager.setTaskExecutor(this.taskExecutor);
//提供对异步处理的支持
asyncManager.setAsyncWebRequest(asyncWebRequest);
//异步调用拦截器
asyncManager.registerCallableInterceptors(this.callableInterceptors);
asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);
//异步处理完成 获取异步执行结果
Object result = asyncManager.getConcurrentResult();
mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
asyncManager.clearConcurrentResult();
//!!!替换invocableMethod(原先HandlerMethod中返回值是Callable现在直接返回结果,无需进行参数解析)
invocableMethod = invocableMethod.wrapConcurrentResult(result);
//对invocableMethod进行参数解析,过程调用,返回值转化
//并将结果存到mavContainer中
invocableMethod.invokeAndHandle(webRequest, mavContainer);
//从mavContainer捞出结果
return getModelAndView(mavContainer, modelFactory, webRequest);
复制代码