An example of how springmvc handles asynchronous requests

  • 2020-06-01 09:56:58
  • OfStack

From the beginning, springmvc 3.2 supports asynchronous requests for servlet 3.0. Normally, we request 1 controller1 synchronously. If there is a time-consuming business operation in the code execution, the servlet container thread will be locked and blocked when other requests come in.

After springmvc 3.2, asynchronous requests are supported and one Callable or DeferredResult can be returned in controller. When Callable is returned, the approximate execution process is as follows:

When the return value of controller is Callable, springmvc will start a thread and give Callable to TaskExecutor for processing Then DispatcherServlet and all the spring interceptors exit the main thread and leave response open When Callable execution is complete, springmvc will restart to allocate an request request, and DispatcherServlet will re-invoke and process the results of the asynchronous Callable execution, and then return to the view

DeferredResult is similar to Callable except that when DeferredResult is executed by another thread in the application, Callable is executed by TaskExecutor.

springmvc configures asynchronous requests

1. web.xml plus servlet3.0 scheme library is required


<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
...
</web-app>

2. Add servlet and filter in web. xml < asyncsupported > true < /async-supported > Child nodes


<!-- springMVC the Servlet configuration  -->
<servlet>
  <servlet-name>dispatcher</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath*:META-INF/dispatcher-context.xml</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
  <async-supported>true</async-supported>
</servlet>

<!--  Coding to intercept  -->
<filter>
  <filter-name>CharacterEncodingFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <async-supported>true</async-supported>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
  <init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>

3. You can then execute the asynchronous request in controller

Execute the asynchronous request using Callable and return the view


@RequestMapping("/mvc25")
public Callable<String> mvc25() {

  return new Callable<String>() {
    @Override
    public String call() throws Exception {
       Thread.sleep(2000);
       return "task/task";
    }
  };

}

The asynchronous request is executed using Callable, and the result of the request is transformed by httpmessageconverter via @response back to the client


@RequestMapping("/mvc26")
@ResponseBody
public Callable<String> mvc26() {

  return new Callable<String>() {
    @Override
    public String call() throws Exception {
      Thread.sleep(2000);
      return "hello task";
    }
  };

}

Client timeout can be customized


@RequestMapping("/mvc27")
@ResponseBody
public WebAsyncTask<String> mvc27() {

  Callable<String> callable = new Callable<String>() {
    @Override
    public String call() throws Exception {
      Thread.sleep(10000);
      return "hello task";
    }
  };

  return new WebAsyncTask<String>(10000, callable);
}

If you encounter an exception, a handler, or a normal request during thread execution, you can handle it with @ExceptionHandler or define global HandlerExceptionResolver


@RequestMapping("/mvc28")
@ResponseBody
public Callable<String> mvc28() {

  Callable<String> callable = new Callable<String>() {
    @Override
    public String call() throws Exception {
      Thread.sleep(2000);
      throw new RuntimeException();
    }
  };

  return callable;

}

@ExceptionHandler(RuntimeException.class)
@ResponseBody
public JSONObject handlerException(){

  JSONObject jsonObject = new JSONObject();
  jsonObject.put("aaa", 123);

  return jsonObject ;
}

You can also process the asynchronous request by returning DeferredResult, which returns an instance to another thread.


@RequestMapping("/mvc29")
@ResponseBody
public DeferredResult<String> mvc29() {

  DeferredResult<String> deferredResult = new DeferredResult<String>();
  dealInOtherThread(deferredResult);
  return deferredResult;

}

private void dealInOtherThread(DeferredResult<String> deferredResult) {
  try {
    Thread.sleep(2000);
  } catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }

  deferredResult.setResult("hello task");
}

When dealInOtherThread processing is completed, setResult will trigger springmvc to allocate 1 request to DispatcherServlet, and DispatcherServlet will process the return result of DeferredResult and return to the view.

DeferredResult also provides other returns to handle thread requests, such as onTimeout(Runnable) and onCompletion(Runnable), onTimeout can register a thread callback, the callback function when a request is delayed, onCompletion can register a callback function when a request is completed.


Related articles: