阅读 94

SpringMVC源码解析(6)-异步处理

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();
  }
}
复制代码

这里异步的处理 针对两次请求有两种处理

  1. 第一次请求: 开始异步请求

    //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);
复制代码
评论