多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
### 返回Callable Spring MVC 3.2开始引入了基于Servlet 3的异步请求处理。相比以前,控制器方法已经不一定需要返回一个值,而是可以返回一个java.util.concurrent.Callable的对象,并通过Spring MVC所管理的线程来产生返回值 ``` @Component public class TaskService { public String execute() { try { TimeUnit.SECONDS.sleep(15); } catch (InterruptedException e) { e.printStackTrace(); } return "hello"; } } @RestController public class AsyncCallableController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final TaskService taskService; @Autowired public AsyncCallableController(TaskService taskService) { this.taskService = taskService; } @RequestMapping(value = "/callable", method = RequestMethod.GET, produces = "text/html") public Callable<String> executeSlowTask() { logger.info("Request received"); Callable<String> callable = taskService::execute; logger.info("Servlet thread released"); return callable; } } output===> Request received Servlet thread released Slow task executed ``` Callable的异步请求被处理时所依次发生的事件: 1. 控制器先返回一个Callable对象 2. Spring MVC开始进行异步处理,并把该Callable对象提交给另一个独立线程的执行器TaskExecutor处理 3. DispatcherServlet和所有过滤器都退出Servlet容器线程,但此时方法的响应对象仍未返回 4. Callable对象最终产生一个返回结果,此时Spring MVC会重新把请求分派回Servlet容器,恢复处理 5. DispatcherServlet再次被调用,恢复对Callable异步处理所返回结果的处理 ### 返回DeferredResult **DeferedResult处理流程** 1. Spring mvc的控制层接收用户的请求之后,如果要采用异步处理,那么就要返回DeferedResult&lt;&gt;泛型对象。在调用完控制层之后,立即回返回DeferedResult对象,此时驱动控制层的容器主线程,可以处理更多的请求 2. 可以将DeferedResult对象作为真实响应数据的代理,而真实的数据是该对象的成员变量result,它可以是String类型,或者ModelAndView类型等 3. 容器主线程,会调用DeferedResult对象的getResult方法,然后响应到客户端。在业务没有处理完毕时,result真实数据还没有形成,那么容器主线程会发生阻塞 4. 业务处理完毕之后,要执行setResult方法,将真实的响应数据赋值到DeferedResult对象中。此时,异步线程会唤醒容器主线程。那么容器主线程会继续执行getResult方法,将真实数据响应到客户端 ``` @RestController public class AsyncDeferredController { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final TaskService taskService; @Autowired public AsyncDeferredController(TaskService taskService) { this.taskService = taskService; } @RequestMapping(value = "/deferred", method = RequestMethod.GET, produces = "text/html") public DeferredResult<String> executeSlowTask() { logger.info("Request received"); DeferredResult<String> deferredResult = new DeferredResult<>(); CompletableFuture.supplyAsync(taskService::execute) .whenCompleteAsync((result, throwable) -> deferredResult.setResult(result)); logger.info("Servlet thread released"); return deferredResult; } } ``` > 在Spring Mvc的控制层中,只要有一个用户请求便会实例化一个DeferedResult对象,然后返回该对象,进行响应客户端。只要DeferedResult对象不设置result响应的内容,则控制层的容器主线程在响应客户端上就会发生阻塞。因为SpringMVC只会实例化一个Controller对象,无论有多少个用户请求,在堆上只有一个Controller对象,因此可以添加一个成员变量List,将这些用户请求的DeferedResult对象存放到List中,然后启动一个定时线程扫描list,从而依次执行setResult方法,响应客户端 ``` @Controller public class DeferedResultController { private ConcurrentLinkedDeque<DeferredResult<String>> deferredResults = new ConcurrentLinkedDeque<DeferredResult<String>>(); @RequestMapping("/getResult") @ResponseBody public DeferredResult<String> getDeferredResultController(){ //设置 5秒就会超时 final DeferredResult<String> stringDeferredResult = new DeferredResult<String>(1000); //将请求加入到队列中 deferredResults.add(stringDeferredResult); final String message = "{username:wangbinghua}"; ExecutorService executorService = Executors.newFixedThreadPool(10); executorService.submit(new Runnable() { @Override public void run() { try { Thread.sleep(1010); } catch (InterruptedException e) { e.printStackTrace(); } //业务处理 System.out.println("业务处理"); stringDeferredResult.setResult(message); } }); //setResult完毕之后,调用该方法 stringDeferredResult.onCompletion(new Runnable() { @Override public void run() { System.out.println("异步调用完成"); //响应完毕之后,将请求从队列中去除掉 deferredResults.remove(stringDeferredResult); } }); stringDeferredResult.onTimeout(new Runnable() { @Override public void run() { System.out.println("业务处理超时"); stringDeferredResult.setResult("error:timeOut"); } }); return stringDeferredResult; } //开启线程定时扫描队列,响应客户端 @Scheduled(fixedRate = 1000) public void scheduleResult(){ System.out.println(new Date()); for(int i = 0;i < deferredResults.size();i++){ DeferredResult<String> deferredResult = deferredResults.getFirst(); deferredResult.setResult("result:" + i); } } } ``` ### DeferedResult 两个监听器(onCompletion & onTimeout) * 当DeferedResult对象调用setResult之后,响应完毕客户端,则直接调用onCompletion对应的方法。 * 当业务处理相当耗时,则响应客户端超时,也会调用onCompletion对应的方法以及onTimeout方法。此时,响应客户端的内容为deferedResult.setErrorResult的内容,否则500错误。 * 发生异常,调用onCompletion方法,此时,响应客户端的内容为deferedResult.setErrorResult的内容,否则500错误 ### WebAsyncTask对象使用实例 ``` @RequestMapping("/async") @ResponseBody public WebAsyncTask<String> asyncTask(){ // 1000 为超时设置 WebAsyncTask<String> webAsyncTask = new WebAsyncTask<String>(1000,new Callable<String>(){ @Override public String call() throws Exception { //业务逻辑处理 Thread.sleep(5000); String message = "username:wangbinghua"; return message; } }); webAsyncTask.onCompletion(new Runnable() { @Override public void run() { System.out.println("调用完成"); } }); webAsyncTask.onTimeout(new Callable<String>() { @Override public String call() throws Exception { System.out.println("业务处理超时"); return "<h1>Time Out</h1>"; } }); return webAsyncTask; } ``` ### 总结 Callable和Deferredresult做的是同样的事情——释放容器线程,在另一个线程上异步运行长时间的任务。不同的是谁管理执行任务的线程:Callable执行线程完毕即返回;Deferredresult通过设置返回对象值(deferredResult.setResult\(result\)\);)返回,可以在任何地方控制返回