Spring MVC - Asynchronous Request Processing using Callable

[Updated: Jul 27, 2016, Created: Mar 18, 2016]

Spring supports Servlet 3 based asynchronous request processing

We don't have to use Servlet 3 asynchronous API, instead Spring MVC abstracts away the thread management details from the controllers.

To use this support, the return value of a handler method has to be java.util.concurrent.Callable.

Spring MVC invokes the returned instance of Callable in a separate thread with the help of an underlying org.springframework.core.task.TaskExecutor

The request is dispatched back to the Servlet container to resume processing using the value returned by the Callable.


This example shows how to use this feature of Spring MVC


Creating the Controller

@Controller
public class MyController {
    Logger logger = Logger.getLogger(MyController.class.getSimpleName());

    @RequestMapping("test")
    public @ResponseBody Callable<String> handleTestRequest () {

        logger.info("handler started");
        Callable<String> callable = new Callable<String>() {
            @Override
            public String call () throws Exception {
                logger.info("async task started");
                Thread.sleep(2000);
                logger.info("async task finished");
                return "async result";
            }
        };

        logger.info("handler finished");
        return callable;
    }
}



The Unit test

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = MyWebConfig.class)
public class ControllerTest {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Before
    public void setup () {
        DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
        this.mockMvc = builder.build();
    }

    @Test
    public void testController () throws Exception {

        MockHttpServletRequestBuilder builder =
                            MockMvcRequestBuilders.get("/test");


        this.mockMvc.perform(builder)
                    .andExpect(MockMvcResultMatchers.request()
                                                    .asyncStarted())
                    .andExpect(MockMvcResultMatchers.request()
                                                    .asyncResult("async result"))
                    .andExpect(MockMvcResultMatchers.status()
                                                    .isOk());
    }

}



Output:

On running the test, the following lines of logs shows that the controller method exits immediately and a new thread is spawned by Spring to free the main Servlet container thread.

    Mar 18, 2016 8:48:20 PM org.springframework.test.web.servlet.TestDispatcherServlet initServletBean
    INFO: FrameworkServlet '': initialization completed in 14 ms
    Mar 18, 2016 8:48:20 PM com.logicbig.example.MyController handleTestRequest
    INFO: handler started
    Mar 18, 2016 8:48:20 PM com.logicbig.example.MyController handleTestRequest
    INFO: handler finished
    Mar 18, 2016 8:48:20 PM com.logicbig.example.MyController$1 call
    INFO: async task started
    Mar 18, 2016 8:48:22 PM com.logicbig.example.MyController$1 call
    INFO: async task finished

Example Project

Dependencies and Technologies Used :

  • Spring Web MVC 4.2.4.RELEASE: Spring Web MVC.
  • Java Servlet API 3.0.1
  • Spring TestContext Framework 4.2.4.RELEASE: Spring TestContext Framework.
  • JUnit 4.12: JUnit is a unit testing framework for Java, created by Erich Gamma and Kent Beck.
  • JDK 1.8
  • Maven 3.0.4

Async Callable Select All Download
  • spring-async-processing
    • src
      • main
        • java
          • com
            • logicbig
              • example
      • test
        • java
          • com
            • logicbig
              • example

See Also