了解Spring MVC的DeferredResult类弹簧MVC-GitHub的聊天应用程序上下文 [英] Understanding the Spring MVC's DeferredResult class in the context of the spring-mvc-chat github application

查看:501
本文介绍了了解Spring MVC的DeferredResult类弹簧MVC-GitHub的聊天应用程序上下文的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想更好地理解下面的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; D​​eferredResult&LT;名单,LT;弦乐&GT;&gt;中整数GT; chatRequests =新的ConcurrentHashMap&LT; D​​eferredResult&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; D​​eferredResult&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 =htt​​p://www.w3.org/1999/xhtml的xmlns:TH =htt​​p://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; D​​IV数据绑定=看得见!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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆