了解Spring MVC的DeferredResult类弹簧MVC-GitHub的聊天应用程序上下文 [英] Understanding the Spring MVC's DeferredResult class in the context of the spring-mvc-chat github application
问题描述
我想更好地理解下面的Spring MVC 3.2应用程序是如何工作的:<一href=\"https://github.com/rstoyanchev/spring-mvc-chat\">https://github.com/rstoyanchev/spring-mvc-chat
我的问题是关于<一个href=\"http://static.springsource.org/spring/docs/3.2.x/javadoc-api/org/springframework/web/context/request/async/DeferredResult.html\">deferredResult Spring MVC的类。我注意到,在给定的时间,因为有连接到聊天应用程序的用户中有 chatRequests
地图尽可能多的条目。
说,有 3的用户连接以聊天应用。你会看到,当用户#3发布消息(参见下面的postMessage方法),然后点击 for循环(在postMessage的方法)迭代三次。我想不通这是为什么。
我包括下面的示例code。
code的控制器:
@Controller
@RequestMapping(/ MVC /聊天)
公共类ChatController { 私人最终ChatRepository chatRepository;
私人最终地图&LT; DeferredResult&LT;名单,LT;弦乐&GT;&gt;中整数GT; chatRequests =新的ConcurrentHashMap&LT; DeferredResult&LT;名单,LT;弦乐&GT;&gt;中整数GT;(); @Autowired
公共ChatController(ChatRepository chatRepository){
this.chatRepository = chatRepository;
} @RequestMapping(方法= RequestMethod.GET)
@ResponseBody
公共DeferredResult&LT;名单,LT;弦乐&GT;&GT;的getMessages(@RequestParam INT messageIndex){ 最后DeferredResult&LT;名单,LT;弦乐&GT;&GT; deferredResult =新DeferredResult&LT;名单,LT;弦乐&GT;&GT;(NULL,Collections.emptyList());
this.chatRequests.put(deferredResult,messageIndex); deferredResult.onCompletion(新的Runnable(){
@覆盖
公共无效的run(){
chatRequests.remove(deferredResult);
}
}); 清单&LT;串GT;消息= this.chatRepository.getMessages(messageIndex);
如果(!messages.isEmpty()){
deferredResult.setResult(消息);
} 返回deferredResult;
} @RequestMapping(方法= RequestMethod.POST)
@ResponseBody
公共无效的postMessage(@RequestParam字符串消息){ this.chatRepository.addMessage(消息); //更新所有的聊天请求作为POST请求的一部分
//见的Redis分支的一个更复杂的,无阻塞的方法 对于(进入&LT; DeferredResult&LT;名单,LT;弦乐&GT;&gt;中整数GT;项:this.chatRequests.entrySet()){
清单&LT;串GT;消息= this.chatRepository.getMessages(entry.getValue());
entry.getKey()的setResult(消息)。
}
}
}
的Javascript code:
$(文件)。就绪(函数(){ 功能ChatViewModel(){ VAR认为这=; that.userName = ko.observable('');
that.chatContent = ko.observable('');
that.message = ko.observable('');
that.messageIndex = ko.observable(0);
that.activePollingXhr = ko.observable(NULL);
VAR keepPolling = FALSE; that.joinChat =功能(){
如果(that.userName()。修剪()!=''){
keepPolling = TRUE;
pollForMessages();
}
} 功能pollForMessages(){
如果(!keepPolling){
返回;
}
变种形式= $(#joinChatForm);
that.activePollingXhr($阿贾克斯(网址:{url:form.attr(行动),键入:GET,数据:form.serialize(),缓存:假的,
成功:函数(消息){
的console.log(消息);
对于(VAR I = 0; I&LT; messages.length;我++){
that.chatContent(that.chatContent()+信息[I] +\\ n);
that.messageIndex(that.messageIndex()+ 1);
}
},
错误:功能(XHR){
如果(xhr.statusText =中止和放大器;!&安培;!xhr.status = 503){
resetUI();
console.error(无法获取聊天消息聊天结束了。);
}
},
完成:pollForMessages
}));
$('#消息)专注()。
} that.postMessage =功能(){
如果(that.message()。修剪()!=''){
变种形式= $(#postMessageForm);
$阿贾克斯(网址:{url:form.attr(行动),键入:POST,
数据:消息= [+ that.userName()+]+ $(#postMessageForm输入[名称=消息])VAL()。
错误:功能(XHR){
console.error(错误发布聊天消息:状态=+ xhr.status +,状态文本=+ xhr.statusText);
}
});
that.message('');
}
} that.leaveChat =功能(){
that.activePollingXhr(NULL);
resetUI();
this.userName('');
} 功能resetUI(){
keepPolling = FALSE;
that.activePollingXhr(NULL);
that.message('');
that.messageIndex(0);
that.chatContent('');
} } //激活knockout.js
ko.applyBindings(新ChatViewModel());});
和html页面:
&LT;!DOCTYPE HTML&GT;
&LT; HTML的xmlns =http://www.w3.org/1999/xhtml的xmlns:TH =http://www.thymeleaf.org&GT;
&LT; HEAD&GT;
&LT;标题&GT;聊天和LT; /标题&GT;
&LT; META HTTP-EQUIV =Content-Type的CONTENT =text / html的;字符集= UTF-8/&GT;
&LT; /头&GT;
&LT;身体GT;
&LT; H1&GT;聊天和LT; / H1&GT; &LT;表ID =joinChatForm日:行动=@ {/ MVC /聊天}数据绑定=可见:activePollingXhr()== NULL&GT;
&所述p为H.;
&LT;标签=用户&gt;用户:LT; /标签&gt;
&LT;输入ID =用户NAME =用户类型=文本数据绑定=值:用户名/&GT;
&LT;输入名称=messageIndex类型=隐藏数据绑定=值:messageIndex/&GT;
&LT;按钮ID =启动类型=提交数据绑定=点击:joinChat&GT;加入聊天&LT; /按钮&GT;
&所述; / P&GT;
&LT; /表及GT; &LT;表ID =leaveChatForm日:行动=@ {/ MVC /聊天}数据绑定=可见:activePollingXhr()!= NULL&GT;
&所述p为H.;
你聊天为&lt;强大的数据绑定=TEXT:用户名&GT;&LT; / STRONG&GT;
&LT;按钮ID =离开类型=提交数据绑定=点击:leaveChat&GT;保留聊天&LT; /按钮&GT;
&所述; / P&GT;
&LT; /表及GT; &LT; DIV数据绑定=看得见!activePollingXhr()= NULL&GT;
&LT; textarea的行数=15COLS =60只读=只读数据绑定=TEXT:chatContent&GT;&LT; / textarea的&GT;
&LT; / DIV&GT; &LT;表ID =postMessageForm日:行动=@ {/ MVC /聊天}数据绑定=可见:activePollingXhr()!= NULL&GT;
&所述p为H.;
&LT;输入ID =消息NAME =消息类型=文本数据绑定=值:消息/&GT;
&LT;按钮ID =POST类型=提交数据绑定=点击:postMessage的&GT;邮政和LT; /按钮&GT;
&所述; / P&GT;
&LT; /表及GT;
&LT; /身体GT;
&LT;脚本类型=文/ JavaScript的SRC =../../../资源/ JS / jQuery的-1.7.2.min.js号:SRC =@ {/资源/ JS / jquery- 1.7.2.min.js}&GT;&LT; / SCRIPT&GT;
&LT;脚本类型=文/ JavaScript的SRC =../../../资源/ JS /淘汰赛2.0.0.js号:SRC =@ {/资源/ JS /淘汰赛2.0。 0.js}&GT;&LT; / SCRIPT&GT;
&LT;脚本类型=文/ JavaScript的SRC =../../../资源/ JS / chat.js日:SRC =@ {/资源/ JS / chat.js}&GT;&LT ; / SCRIPT&GT;&LT; / HTML&GT;
我在使用Spring的DeferredResult类的作者详细讨论这个话题,这里是我们谈话的相关部分:
要引用罗森Stoyanchev:
粗略地说。一个DeferredResult是一个开放的要求有关。
当请求完成后,DeferredResult从除去
图,然后,客户端发出一个新的长轮询请求,这
增加了一个新的实例DeferredResult
块引用>I am trying to better understand how the following spring mvc 3.2 application works: https://github.com/rstoyanchev/spring-mvc-chat
My question is about the deferredResult Spring MVC class. I noticed that at a given time there are as many entries in the
chatRequests
Map as there are users connected to the chat application.Say there are 3 users connected to the chat application. You will see that when user #3 posts a message (see postMessage method below), then the for loop (in postMessage method) iterates three times. I can't figure out why that is.
I am including sample code below.
Code for controller:
@Controller @RequestMapping("/mvc/chat") public class ChatController { private final ChatRepository chatRepository; private final Map<DeferredResult<List<String>>, Integer> chatRequests = new ConcurrentHashMap<DeferredResult<List<String>>, Integer>(); @Autowired public ChatController(ChatRepository chatRepository) { this.chatRepository = chatRepository; } @RequestMapping(method = RequestMethod.GET) @ResponseBody public DeferredResult<List<String>> getMessages(@RequestParam int messageIndex) { final DeferredResult<List<String>> deferredResult = new DeferredResult<List<String>>(null, Collections.emptyList()); this.chatRequests.put(deferredResult, messageIndex); deferredResult.onCompletion(new Runnable() { @Override public void run() { chatRequests.remove(deferredResult); } }); List<String> messages = this.chatRepository.getMessages(messageIndex); if (!messages.isEmpty()) { deferredResult.setResult(messages); } return deferredResult; } @RequestMapping(method = RequestMethod.POST) @ResponseBody public void postMessage(@RequestParam String message) { this.chatRepository.addMessage(message); // Update all chat requests as part of the POST request // See Redis branch for a more sophisticated, non-blocking approach for (Entry<DeferredResult<List<String>>, Integer> entry : this.chatRequests.entrySet()) { List<String> messages = this.chatRepository.getMessages(entry.getValue()); entry.getKey().setResult(messages); } } }
Javascript code:
$(document).ready(function() { function ChatViewModel() { var that = this; that.userName = ko.observable(''); that.chatContent = ko.observable(''); that.message = ko.observable(''); that.messageIndex = ko.observable(0); that.activePollingXhr = ko.observable(null); var keepPolling = false; that.joinChat = function() { if (that.userName().trim() != '') { keepPolling = true; pollForMessages(); } } function pollForMessages() { if (!keepPolling) { return; } var form = $("#joinChatForm"); that.activePollingXhr($.ajax({url: form.attr("action"), type: "GET", data: form.serialize(), cache: false, success: function(messages) { console.log(messages); for (var i = 0; i < messages.length; i++) { that.chatContent(that.chatContent() + messages[i] + "\n"); that.messageIndex(that.messageIndex() + 1); } }, error: function(xhr) { if (xhr.statusText != "abort" && xhr.status != 503) { resetUI(); console.error("Unable to retrieve chat messages. Chat ended."); } }, complete: pollForMessages })); $('#message').focus(); } that.postMessage = function() { if (that.message().trim() != '') { var form = $("#postMessageForm"); $.ajax({url: form.attr("action"), type: "POST", data: "message=[" + that.userName() + "] " + $("#postMessageForm input[name=message]").val(), error: function(xhr) { console.error("Error posting chat message: status=" + xhr.status + ", statusText=" + xhr.statusText); } }); that.message(''); } } that.leaveChat = function() { that.activePollingXhr(null); resetUI(); this.userName(''); } function resetUI() { keepPolling = false; that.activePollingXhr(null); that.message(''); that.messageIndex(0); that.chatContent(''); } } //Activate knockout.js ko.applyBindings(new ChatViewModel()); });
and html page:
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title>Chat</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> </head> <body> <h1>Chat</h1> <form id="joinChatForm" th:action="@{/mvc/chat}" data-bind="visible: activePollingXhr() == null"> <p> <label for="user">User: </label> <input id="user" name="user" type="text" data-bind="value: userName"/> <input name="messageIndex" type="hidden" data-bind="value: messageIndex"/> <button id="start" type="submit" data-bind="click: joinChat">Join Chat</button> </p> </form> <form id="leaveChatForm" th:action="@{/mvc/chat}" data-bind="visible: activePollingXhr() != null"> <p> You're chatting as <strong data-bind="text: userName"></strong> <button id="leave" type="submit" data-bind="click: leaveChat">Leave Chat</button> </p> </form> <div data-bind="visible: activePollingXhr() != null"> <textarea rows="15" cols="60" readonly="readonly" data-bind="text: chatContent"></textarea> </div> <form id="postMessageForm" th:action="@{/mvc/chat}" data-bind="visible: activePollingXhr() != null"> <p> <input id="message" name="message" type="text" data-bind="value: message" /> <button id="post" type="submit" data-bind="click: postMessage">Post</button> </p> </form> </body> <script type="text/javascript" src="../../../resources/js/jquery-1.7.2.min.js" th:src="@{/resources/js/jquery-1.7.2.min.js}"></script> <script type="text/javascript" src="../../../resources/js/knockout-2.0.0.js" th:src="@{/resources/js/knockout-2.0.0.js}"></script> <script type="text/javascript" src="../../../resources/js/chat.js" th:src="@{/resources/js/chat.js}"></script> </html>
解决方案I discussed this topic at length with the author of Spring's DeferredResult class and here is the relevant part of our conversation:
To quote Rossen Stoyanchev:
Roughly speaking. A DeferredResult is associated with an open request. When the request completes, the DeferredResult is removed from the map, and then, the client issues a new long polling request, which adds a new DeferredResult instance
这篇关于了解Spring MVC的DeferredResult类弹簧MVC-GitHub的聊天应用程序上下文的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!