Spring WebSocket教程(二)
实现目标
第一步:聊天实现原理
第二步:服务端基础代码
- public class UserChatCommand {
- private String name;
- private String chatContent;
- private String coordinationId;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getChatContent() {
- return chatContent;
- }
- public void setChatContent(String chatContent) {
- this.chatContent = chatContent;
- }
- public String getCoordinationId() {
- return coordinationId;
- }
- public void setCoordinationId(String coordinationId) {
- this.coordinationId = coordinationId;
- }
- @Override
- public String toString() {
- return "UserChatCommand{" +
- "name='" + name + '\'' +
- ", chatContent='" + chatContent + '\'' +
- ", coordinationId='" + coordinationId + '\'' +
- '}';
- }
- }
通过这个bean来接收到web端发送的消息,然后在服务端转发,接下来就是转发的逻辑了,不过首先需要介绍一下Spring WebSocket的一个annotation。
- /**
- * WebSocket聊天的相应接收方法和转发方法
- *
- * @param userChat 关于用户聊天的各个信息
- */
- @MessageMapping("/userChat")
- public void userChat(UserChatCommand userChat) {
- //找到需要发送的地址
- String dest = "/userChat/chat" + userChat.getCoordinationId();
- //发送用户的聊天记录
- this.template.convertAndSend(dest, userChat);
- }
怎么这么简单?呵呵,能够这么简单的实现后台代码,全是Spring的功劳。首先,我们约定好发送地址的规则,就是chat后面跟上之前发送过来的id,然后通过这个“template”来进行转发,这个“template”是Spring实现的一个发送模板类:SimpMessagingTemplate,在我们定义controller的时候,可以在构造方法中进行注入:
- @Controller
- public class CoordinationController {
- ......
- //用于转发数据(sendTo)
- private SimpMessagingTemplate template;
- <pre name="code" class="java"> @Autowired
- public CoordinationController(SimpMessagingTemplate t) {
- template = t;
- }
- .....
- }
现在就已经将用户发送过来的聊天信息转发到了一个约定的空间内,只要web端的用户订阅的是这个空间的地址,那么就会收到转发过来的json。现在来看看web端需要做什么吧。
第三步:Web端代码

- //发送聊天信息
- function sendName() {
- var input = $('#chat_input');
- var inputValue = input.val();
- input.val("");
- stompClient.send("/app/userChat", {}, JSON.stringify({
- 'name': encodeURIComponent(name),
- 'chatContent': encodeURIComponent(inputValue),
- 'coordinationId': coordinationId
- }));
- }
其中,name和coordinationId是相应的用户信息,可以通过ajax或者jsp获取,这里就不多说了。
- //用户聊天订阅
- stompClient.subscribe('/userChat/chat' + coordinationId, function (chat) {
- showChat(JSON.parse(chat.body));
- });
将消息体转为json,再写一个显示聊天信息的方法就可以了,显示聊天信息的方法不再解释,如下:
- //显示聊天信息
- function showChat(message) {
- var response = document.getElementById('chat_content');
- response.value += decodeURIComponent(message.name) + ':' + decodeURIComponent(message.chatContent) + '\n';
- }
因为之前处理中文问题,所以发到后台的数据是转码了的,从后台发回来之后,也需要将编码转回来。
第四步:聊天记录缓存实现
- private Map<Integer, Object[]> coordinationCache = new HashMap<Integer, Object[]>();
这里我存的是一个Object数组,是因为我写的程序中,除了聊天信息的缓存,还有很多东西要缓存,只是将聊天信息的缓存放在了这个数组中的一个位置里。

- private int limit;
- private Queue<E> queue;
limit代表队列的上限,queue是真正使用的队列。创建一个由这两个参数形成的构造方法,并且实现Queue的所有方法,所有的方法都由queue对象去完成,比如:
- @Override
- public int size() {
- return queue.size();
- }
- @Override
- public boolean isEmpty() {
- return queue.isEmpty();
- }
其中,有一个方法需要做处理:
- @Override
- public boolean offer(E e) {
- if (queue.size() >= limit) {
- queue.poll();
- }
- return queue.offer(e);
- }
加入元素的时候,判断是否达到了上限,达到了的话就先出队列,再入队列。这样,就实现了固定大小的队列,并且总是保持最新的记录。
- /**
- * WebSocket聊天的相应接收方法和转发方法
- *
- * @param userChat 关于用户聊天的各个信息
- */
- @MessageMapping("/userChat")
- public void userChat(UserChatCommand userChat) {
- //找到需要发送的地址
- String dest = "/userChat/chat" + userChat.getCoordinationId();
- //发送用户的聊天记录
- this.template.convertAndSend(dest, userChat);
- //获取缓存,并将用户最新的聊天记录存储到缓存中
- Object[] cache = coordinationCache.get(Integer.parseInt(userChat.getCoordinationId()));
- try {
- userChat.setName(URLDecoder.decode(userChat.getName(), "utf-8"));
- userChat.setChatContent(URLDecoder.decode(userChat.getChatContent(), "utf-8"));
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- ((LimitQueue<UserChatCommand>) cache[1]).offer(userChat);
- }
已经有缓存了,只要在页面上取出缓存就能显示聊天记录了,可以通过ajax或者jsp等方法,不过,WebSocket也有方法可以实现,因为Spring WebSocket提供了一个叫SubscribeMapping的annotation,这个annotation标记的方法,是在订阅的时候调用的,也就是说,基本是只执行一次的方法,很适合我们来初始化聊天记录。所以,在订阅聊天信息的代码下面,可以增加一个初始化聊天记录的方法。我们先写好web端的代码:
- //初始化
- stompClient.subscribe('/app/init/' + coordinationId, function (initData) {
- console.log(initData);
- var body = JSON.parse(initData.body);
- var chat = body.chat;
- chat.forEach(function(item) {
- showChat(item);
- });
- });
这次订阅的地址是init,还是加上coordinationId来区分空间,发送过来的数据是一个聊天记录的数组,循环显示在对话框中。有了web端代码的约束,后台代码也基本出来了,只要使用SubscribeMapping,再组装一下数据就完成了,后台代码如下:
- /**
- * 初始化,初始化聊天记录
- *
- * @param coordinationId 协同空间的id
- */
- @SubscribeMapping("/init/{coordinationId}")
- public Map<String,Object> init(@DestinationVariable("coordinationId") int coordinationId) {
- System.out.println("------------新用户进入,空间初始化---------");
- Map<String, Object> document = new HashMap<String, Object>();
- document.put("chat",coordinationCache.get(coordinationId)[1]);
- return document;
- }
就这样,缓存聊天记录也实现了。
结语

Spring WebSocket教程(二)的更多相关文章
- Spring Security教程(二):自定义数据库查询
Spring Security教程(二):自定义数据库查询 Spring Security自带的默认数据库存储用户和权限的数据,但是Spring Security默认提供的表结构太过简单了,其实就 ...
- Spring Security教程(二):通过数据库获得用户权限信息
上一篇博客中,Spring Security教程(一):初识Spring Security,我把用户信息和权限信息放到了xml文件中,这是为了演示如何使用最小的配置就可以使用Spring Securi ...
- Spring Security教程(二)
上一篇博客中,Spring Security教程(一),我把用户信息和权限信息放到了xml文件中,这是为了演示如何使用最小的配置就可以使用Spring Security,而实际开发中,用户信息和权限信 ...
- Spring WebSocket教程(一)
学习背景 很久以前就知道WebSocket,但那时不论是浏览器还是开发技术对它的支持都还很少.但是,Spring4突然发布,让我眼前一亮,Spring4直接支持WebSocket. 对于Spring我 ...
- WebSocket教程(二)
运行环境:jdk8 tomcat8 无须其他jar包. package com.reach.socketController; import java.io.IOException; import j ...
- Spring WebSocket入门(二) 转载
本文转载自:http://www.jianshu.com/p/8500ad65eb50 WebSocket前端准备 前端我们需要用到两个js文件:sockjs.js和stomp.js SockJS:S ...
- web即时通讯2--基于Spring websocket达到web聊天室
如本文所用,Spring4和websocket要构建web聊天室,根据框架SpringMVC+Spring+Hibernate的Maven项目,后台使用spring websocket进行消息转发和聊 ...
- Spring Security教程(三):自定义表结构
在上一篇博客中讲解了用Spring Security自带的默认数据库存储用户和权限的数据,但是Spring Security默认提供的表结构太过简单了,其实就算默认提供的表结构很复杂,也不一定能满足项 ...
- Spring Security教程(三)
在上一篇博客中讲解了用Spring Security自带的默认数据库存储用户和权限的数据,但是Spring Security默认提供的表结构太过简单了,其实就算默认提供的表结构很复杂,也不一定能满足项 ...
随机推荐
- 真正理解 git fetch, git pull 以及 FETCH_HEAD
真正理解 git fetch, git pull 要讲清楚git fetch,git pull,必须要附加讲清楚git remote,git merge .远程repo, branch . commi ...
- java与C++之间进行SOCKET通讯要点简要解析
原文链接: http://blog.csdn.net/hslinux/article/details/6214594 java与C++之间进行SOCKET通讯要点简要解析 hslinux 0.篇外语 ...
- SharePoint 2013 Farm 安装指南——构建一个双层SharePoint Farm
最近要对公司里的SharePoint进行升级,由于旧的系统SharePoint 2010已经有2年了,上面改动比较多,而且权限也很混乱了,所以下定决心要对其做一次升级,重新部署一台新的SharePoi ...
- python selenium 使用unittest 示例
python selenium 使用unittest 示例 并等待某个元素示例 from selenium.webdriver.support.ui import WebDriverWait from ...
- 修改hadoop FileUtil.java,解决权限检查的问题
在Hadoop Eclipse开发环境搭建这篇文章中,第15.)中提到权限相关的异常,如下: 15/01/30 10:08:17 WARN util.NativeCodeLoader: Una ...
- xcode自动打ipa包脚本 资料
http://webfrogs.me/2012/09/19/buildipa/ http://blog.csdn.net/baxiaxx/article/details/8267295 http:// ...
- vivado中设置多线程编译
VIVADO中一个run编译时支持的线程数如下表:(综合时一般是2线程) Place Route Windows默认 2 2 Linux默认 4 4 Windows开启maxThreads=8 4 4 ...
- MinGW环境libssh2安装
由于实习工作中要用到基于sftp协议开发一个网络程序,同时要实现运行在Windows平台上,找来找去就这个libssh2库好用,在网络上算是有那么一点点的文档可以看.这个库还不是现成的,还要进行源代码 ...
- OpenCV中图像算术操作与逻辑操作
OpenCV中图像算术操作与逻辑操作 在图像处理中有两类最重要的基础操作各自是图像点操作与块操作.简单点说图像点操作就是图像每一个像素点的相关逻辑与几何运算.块操作最常见就是基于卷积算子的各种操作.实 ...
- [Windows Azure] How to use the Windows Azure Blob Storage Service in .NET
How to use the Windows Azure Blob Storage Service in .NET version 1.7 version 2.0 This guide will de ...