springmvc异步请求的实现原理

一、mvc异步请求的主要3种方式:

  (1)Callable

@PostMapping
public Callable<String> processUpload(final MultipartFile file) {

    return new Callable<String>() {
        public String call() throws Exception {
            // ...
            return "someView";
        }
    };

}

  (2)DeferredResult

@GetMapping("/quotes")
@ResponseBody
public DeferredResult<String> quotes() {
    DeferredResult<String> deferredResult = new DeferredResult<String>();
    // Save the deferredResult somewhere..
    return deferredResult;
}

// From some other thread...
deferredResult.setResult(data);

  (3)HTTP Streaming

@GetMapping("/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();

二、springmvc的异步请求的本质,其实就是对servlet3.0的异步servlet的封装

  @RequestMapping(value = "/AsyncServlet",method = RequestMethod.GET)
    public void asyncServlet(HttpServletRequest request) throws IOException {
        //开启异步请求
        AsyncContext asyncContext = request.startAsync();
        //设置超时时间
        asyncContext.setTimeout(60*1000L);
        asyncContext.addListener(new AsyncListener() {
            @Override
            public void onComplete(AsyncEvent asyncEvent) throws IOException {
                System.out.println("异步请求完成");
            }

            @Override
            public void onTimeout(AsyncEvent asyncEvent) throws IOException {
            }

            @Override
            public void onError(AsyncEvent asyncEvent) throws IOException {
            }
            @Override
            public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
            }
        });
        ServletResponse response = asyncContext.getResponse();
        // Save the response somewhere..
    }
  
  // From some other thread...
  //response 响应内容给客户端

三、springmvc的封装(以DeferredResult为例)

  通过方法的返回值处理器DeferredResultMethodReturnValueHandler

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) 
              throws Exception { if (returnValue == null) { mavContainer.setRequestHandled(true); } else { DeferredResult result; if (returnValue instanceof DeferredResult) { result = (DeferredResult)returnValue; } else if (returnValue instanceof ListenableFuture) { result = this.adaptListenableFuture((ListenableFuture)returnValue); } else { if (!(returnValue instanceof CompletionStage)) { throw new IllegalStateException("Unexpected return value type: " + returnValue); } result = this.adaptCompletionStage((CompletionStage)returnValue); }        //主要代码,调用AsyncManager开启DeferredResult流程
       WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(result,
new Object[]{mavContainer}); } }

  然后调用WebAsyncManager#startDeferredResultProcessing方法实现:

public void startDeferredResultProcessing(DeferredResult<?> deferredResult, Object... processingContext) throws Exception {
        Assert.notNull(deferredResult, "DeferredResult must not be null");
        Assert.state(this.asyncWebRequest != null, "AsyncWebRequest must not be null");
        Long timeout = deferredResult.getTimeoutValue();
        if (timeout != null) {
            this.asyncWebRequest.setTimeout(timeout);
        }

        List<DeferredResultProcessingInterceptor> interceptors = new ArrayList();
        interceptors.add(deferredResult.getInterceptor());
        interceptors.addAll(this.deferredResultInterceptors.values());
        interceptors.add(timeoutDeferredResultInterceptor);
        DeferredResultInterceptorChain interceptorChain = new DeferredResultInterceptorChain(interceptors);
        this.asyncWebRequest.addTimeoutHandler(() -> {
            try {
                interceptorChain.triggerAfterTimeout(this.asyncWebRequest, deferredResult);
            } catch (Throwable var4) {
                this.setConcurrentResultAndDispatch(var4);
            }

        });
        this.asyncWebRequest.addErrorHandler((ex) -> {
            try {
                if (!interceptorChain.triggerAfterError(this.asyncWebRequest, deferredResult, ex)) {
                    return;
                }

                deferredResult.setErrorResult(ex);
            } catch (Throwable var5) {
                this.setConcurrentResultAndDispatch(var5);
            }

        });
        this.asyncWebRequest.addCompletionHandler(() -> {
            interceptorChain.triggerAfterCompletion(this.asyncWebRequest, deferredResult);
        });
        interceptorChain.applyBeforeConcurrentHandling(this.asyncWebRequest, deferredResult);
     //开启异步请求就是,request.startAsync();
this.startAsyncProcessing(processingContext); try { interceptorChain.applyPreProcess(this.asyncWebRequest, deferredResult); deferredResult.setResultHandler((result) -> { result = interceptorChain.applyPostProcess(this.asyncWebRequest, deferredResult, result); this.setConcurrentResultAndDispatch(result); }); } catch (Throwable var7) { this.setConcurrentResultAndDispatch(var7); } }

   其中很重要的就是this.asyncWebRequest,查看发现是StandardServletAsyncWebRequest,而StandardServletAsyncWebRequest是实现了AsyncListener的,就是servlet中asyncContext#addListener

方法的参数中的监听器,其中开启异步的代码如下:

   public void startAsync() {
        Assert.state(this.getRequest().isAsyncSupported(), "Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding \"<async-supported>true</async-supported>\" to servlet and filter declarations in web.xml.");
        Assert.state(!this.isAsyncComplete(), "Async processing has already completed");
        if (!this.isAsyncStarted()) {
            this.asyncContext = this.getRequest().startAsync(this.getRequest(), this.getResponse());
            this.asyncContext.addListener(this);
            if (this.timeout != null) {
                this.asyncContext.setTimeout(this.timeout);
            }

        }
    }

发现其实就是servlet中异步的方法。

posted @ 2019-05-27 17:10  fantastiLi  阅读(573)  评论(0)    收藏  举报