文章出處
View Code
View Code
View Code
View Code
View Code
文章列表
Web應用中,有時會遇到一些耗時很長的操作(比如:在后臺生成100張報表再呈現,或 從ftp下載若干文件,綜合處理后再返回給頁面下載),用戶在網頁上點完按鈕后,通常會遇到二個問題:頁面超時、看不到處理進度。
對于超時,采用異步操作,可以很好的解決這個問題,后臺服務收到請求后,執行異步方法不會阻塞線程,因此就不存在超時問題。但是異步處理的進度用戶也需要知道,否則不知道后臺的異步處理何時完成,用戶無法決定接下來應該繼續等候? or 關掉頁面?
思路:
1、browser -> Spring-MVC Controller -> call 后臺服務中的異步方法 -> 將執行進度更新到redis緩存 -> 返回view
2、返回的view頁面上,ajax -> 輪詢 call 后臺服務 -> 查詢redis中的進度緩存數據,并實時更新UI進度顯示 -> 如果完成 call 后臺服務清理緩存
注:這里采用了redis保存異步處理的執行進度,也可以換成session或cookie來保存。
步驟:
一、spring配置文件中,增加Task支持

1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:task="http://www.springframework.org/schema/task" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans.xsd 7 http://www.springframework.org/schema/task 8 http://www.springframework.org/schema/task/spring-task.xsd"> 9 10 11 12 <!-- 支持異步方法執行 --> 13 <task:annotation-driven/> 14 15 </beans>
二、后臺Service中,在方法前加上@Async
先定義服務接口:

1 package ctas.web.service; 2 3 public interface AsyncService { 4 5 /** 6 * 異步執行耗時較長的操作 7 * 8 * @param cacheKey 9 * @throws Exception 10 */ 11 void asyncMethod(String cacheKey) throws Exception; 12 13 /** 14 * 獲取執行進度 15 * 16 * @param cacheKey 17 * @return 18 * @throws Exception 19 */ 20 String getProcess(String cacheKey) throws Exception; 21 22 /** 23 * 執行完成后,清除緩存 24 * 25 * @param cacheKey 26 * @throws Exception 27 */ 28 void clearCache(String cacheKey) throws Exception; 29 }
服務實現:

1 package ctas.web.service.impl; 2 import ctas.web.service.AsyncService; 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.data.redis.core.StringRedisTemplate; 5 import org.springframework.scheduling.annotation.Async; 6 import org.springframework.stereotype.Service; 7 8 @Service("asyncService") 9 public class AsyncServiceImpl extends BaseServiceImpl implements AsyncService { 10 11 @Autowired 12 StringRedisTemplate stringRedisTemplate; 13 14 15 @Override 16 @Async 17 public void asyncMethod(String cacheKey) throws Exception { 18 //模擬總有20個步驟,每個步驟耗時2秒 19 int maxStep = 20; 20 for (int i = 0; i < maxStep; i++) { 21 Thread.sleep(2000); 22 //將執行進度放入緩存 23 stringRedisTemplate.opsForValue().set(cacheKey, (i + 1) + "/" + maxStep); 24 } 25 } 26 27 @Override 28 public String getProcess(String cacheKey) throws Exception { 29 return stringRedisTemplate.opsForValue().get(cacheKey); 30 } 31 32 @Override 33 public void clearCache(String cacheKey) throws Exception { 34 //完成后,清空緩存 35 stringRedisTemplate.delete(cacheKey); 36 } 37 38 39 }
注意:asyncMethod方法前面的@Async注解,這里模擬了一個耗時的操作,并假設要完成該操作,共需要20個小步驟,每執行完一個步驟,將進度更新到redis緩存中。
三、Controller的處理

1 @RequestMapping(value = "async/{key}") 2 public String asyncTest(HttpServletRequest req, 3 HttpServletResponse resp, @PathVariable String key) throws Exception { 4 asyncService.asyncMethod(key); 5 return "common/async"; 6 } 7 8 @RequestMapping(value = "async/{key}/status") 9 public String showAsyncStatus(HttpServletRequest req, 10 HttpServletResponse resp, @PathVariable String key) throws Exception { 11 String status = asyncService.getProcess(key); 12 ResponseUtil.OutputJson(resp, "{\"status\":\"" + status + "\"}"); 13 return null; 14 } 15 16 @RequestMapping(value = "async/{key}/clear") 17 public String clearAsyncStatus(HttpServletRequest req, 18 HttpServletResponse resp, @PathVariable String key) throws Exception { 19 asyncService.clearCache(key); 20 ResponseUtil.OutputJson(resp, "{\"status\":\"ok\"}"); 21 return null; 22 }
四、view上的ajax處理

1 <script type="text/javascript" language="JavaScript"> 2 3 var timerId = null;//定時器ID 4 5 $(document).ready(function () { 6 7 /* 8 定時輪詢執行進度 9 */ 10 timerId = setInterval(function () { 11 getStatus(); 12 }, 1000); 13 getStatus(); 14 }); 15 16 /** 17 獲取執行進度 18 */ 19 function getStatus() { 20 var statusUrl = window.location.href + "/status"; 21 $.get(statusUrl, function (data) { 22 if (data == null || data.status == null || data.status == "null") { 23 updateStatus("準備中"); 24 return; 25 } 26 var status = data.status; 27 updateStatus(status); 28 var temp = status.split("/"); 29 if (temp[0] == temp[1]) { 30 updateStatus("完成"); 31 clearInterval(timerId);//停止定時器 32 clearStatus();//清理redis緩存 33 } 34 }) 35 } 36 37 /** 38 * 執行完成后,清理緩存 39 */ 40 function clearStatus() { 41 var clearStatusUrl = window.location.href + "/clear"; 42 $.get(clearStatusUrl, function (data) { 43 //alert(data.status); 44 }) 45 } 46 47 /** 48 更新進度顯示 49 */ 50 function updateStatus(msg) { 51 $("#status").html(msg); 52 } 53 </script> 54 <div id="msgBox"> 55 <span>請稍候,服務器正在處理中...</span> 56 57 <h1>當前處理進度:<span style="color:red" id="status">準備中</span></h1> 58 </div>
瀏覽 http://localhost:8080/xxx/async/123123后的效果
文章列表
全站熱搜