1、環境簡介前端
項目之前使用的是RCP框架,現想支持web請求,爲了減小後臺邏輯處理工做量,重用以前的RCP程序代碼,經過添加一個新的插件用於處理web請求。具體實現可見http://blog.csdn.net/rongyongfeikai2/article/details/39577237。java
2、問題現象web
在前臺的一個頁面中有兩種ajax請求device和plan。頁面顯示的表格內容是經過plan請求填充,操做須要用到的數據經過定時的device請求來獲取。這兩個請求的後臺處理類爲同一個HttpServlet類。在重複刷新的過程當中,出現plan請求失敗的現象,出現請求失敗時device請求也會出現異常,但成功返回。ajax
關鍵代碼以下:json
AbstractServlet類跨域
1 import javax.servlet.http.HttpServlet; 2 import javax.servlet.http.HttpServletRequest; 3 import javax.servlet.http.HttpServletResponse; 4 5 public abstract class AbstractServlet extends HttpServlet { 6 7 /** 請求*/ 8 protected HttpServletRequest request; 9 10 /** 響應*/ 11 protected HttpServletResponse response; 12 13 /** 操做名稱*/ 14 protected String action; 15 16 /** 請求結果*/ 17 protected boolean result; 18 19 @Override 20 protected void doGet(HttpServletRequest request, HttpServletResponse response){ 21 this.request = request; 22 this.response = response; 23 24 getActionParam(); 25 26 excetue(); 27 28 returnResult(); 29 30 } 31 32 /** 33 * 獲取前端的action字段 34 */ 35 protected void getActionParam() { 36 action = request.getParameter("action"); 37 } 38 39 40 abstract public void excetue(); 41 42 abstract protected void returnResult(); 43 }
TestServlet類瀏覽器
1 import java.io.IOException; 2 3 import org.codehaus.jackson.JsonNode; 4 5 import com.macrosan.core.nonui.util.JsonMapper; 6 7 public class TestServlet extends AbstractServlet { 8 9 private JsonNode resultNode; 10 11 @Override 12 public void excetue() { 13 if ("plan".equals(action)) { 14 queryAllPlans(); 15 } else if ("device".equals(action)) { 16 queryAllDevices(); 17 } 18 } 19 20 private void queryAllDevices() { 21 /// ....... 22 resultNode = JsonMapper.toNormalTree("device response json string"); 23 24 } 25 26 private void queryAllPlans() { 27 /// ....... 28 resultNode = JsonMapper.toNormalTree("plan response json string"); 29 } 30 31 /** 32 * 33 */ 34 protected void returnResult() { 35 36 try { 37 String resultStr = resultNode.toString(); 38 39 String callBack = request.getParameter("callback"); 40 41 // 普通訪問 42 String responseStr = ""; 43 if (callBack == null) { 44 responseStr = resultStr; 45 } else { 46 responseStr = callBack + "(" + resultStr + ")"; 47 } 48 49 response.getWriter().print(responseStr); 50 } catch (IOException e) { 51 e.printStackTrace(); 52 } 53 } 54 }
3、問題定位多線程
復現問題後,經過瀏覽器調試工具,找出後臺返回的結果,以下:app
jQuery110102986526135296296_1444812367854([{plan}])jQuery110102986526135296296_1444812367854([{device1},{device2}])框架
正常狀況下應該只返回一個jQuery110102986526135296296_1444812367854字符串,很顯然是一個請求結果中包含了兩個請求的結果,因爲是jsonp跨域請求,因此第二個ajax請求失敗。那第一個請求出現error的緣由是什麼呢?
從結果上看,應該是plan請求先來處理完成後在returnResult方法中將結果先寫入,且device請求也來了,接着講device的結果也寫入致使的該字符串。
習慣了使用Struts2等框架,每次action請求都是一個單獨的實例來處理請求,跟上一次請求無關。但servlet插件則不一樣,每次處理請求的實例是同一個對象,因此上一次請求將該實例中的屬性修改了會影響到下一次請求。該問題的出現緣由是plan請求先來,request和response都是plan請求的,當完成數據收集後,device請求來了,將request和response修改成device的信息,致使plan請求流程中的returnResult方法中的response是device請求的,故致使device請求的結果有兩個字符串,而plan請求因爲沒有返回數據致使請求失敗。
4、問題解決
解決方法很是簡單,在doGet方法前添加同步關鍵字,以下:
1 @Override 2 synchronized protected void doGet(HttpServletRequest request, HttpServletResponse response){ 3 this.request = request; 4 this.response = response; 5 6 getActionParam(); 7 8 excetue(); 9 10 returnResult(); 11 12 }
5、問題總結 該問題包含多線程處理同一對象,致使信息達不到預期的效果;請求沒有任何返回會做爲error處理;jsonp結果字符串必需要嚴格的格式等知識點。