最近遇到一个需求,首页的待办任务要求实时刷新。刚开始的时候再前端写了一个定时器轮训查询。但是过了一段时间之后觉得太LOW逼了,正好想到了websocket,准备试验一下,废话不说,上代码:
(注:这里使用maven方式添加 手动添加的同学请自行下载相应jar包放到lib目录,本文使用的版本为4.3.5.RELEASE)
<!-- spring websocket-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
在spring配置文件中添加如下代码
<!-- websocket相关扫描,主要扫描:WebSocketConfig 如果前面配置能扫描到此类则可以不加 -->
<context:component-scan base-package="org.zwc.ssm" />
package com.mlkj.common.websocket;
import com.mlkj.common.config.Global;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
* Component注解告诉SpringMVC该类是一个SpringIOC容器下管理的类
* 其实@Controller, @Service, @Repository是@Component的细化
@Component
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
@Autowired
WebSocketHandler handler;
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
//添加websocket处理器,添加握手拦截器
webSocketHandlerRegistry.addHandler(handler, Global.getAdminPath()+"/ws").addInterceptors(new HandShakeInterceptor());
//添加websocket处理器,添加握手拦截器
webSocketHandlerRegistry.addHandler(handler, Global.getAdminPath()+"/ws/sockjs").addInterceptors(new HandShakeInterceptor()).withSockJS();
首先定义一个WebSocketConfig 继承 WebMvcConfigurerAdapter 以及实现 WebSocketConfigurer接口 ,使用注解@EnableWebSocket 表示这是一个webSocket
2 定义 WebSocketHandler
package com.mlkj.common.websocket;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mlkj.common.result.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@Component
public class WebSocketHandler implements org.springframework.web.socket.WebSocketHandler {
//当MyWebSocketHandler类被加载时就会创建该Map,随类而生
public static final Map<String, WebSocketSession> userSocketSessionMap;
static {
userSocketSessionMap = new HashMap<String, WebSocketSession>();
//握手实现连接后
@Override
public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {
String uid = (String) webSocketSession.getAttributes().get("uid");
userSocketSessionMap.putIfAbsent(uid, webSocketSession);
//发送信息前的处理
@Override
public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {
if(webSocketMessage.getPayloadLength()==0)return;
//TODO 得到Socket通道中的数据并转化为Message对象
//TODO 将信息保存至数据库
//发送Socket信息
sendMessageToUser("1", new TextMessage(new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create().toJson(new Result())));
@Override
public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {
* 在此刷新页面就相当于断开WebSocket连接,原本在静态变量userSocketSessionMap中的
* WebSocketSession会变成关闭状态(close),但是刷新后的第二次连接服务器创建的
* 新WebSocketSession(open状态)又不会加入到userSocketSessionMap中,所以这样就无法发送消息
* 因此应当在关闭连接这个切面增加去除userSocketSessionMap中当前处于close状态的WebSocketSession,
* 让新创建的WebSocketSession(open状态)可以加入到userSocketSessionMap中
* @param webSocketSession
* @param closeStatus
* @throws Exception
@Override
public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {
System.out.println("WebSocket:"+webSocketSession.getAttributes().get("uid")+"close connection");
Iterator<Map.Entry<String,WebSocketSession>> iterator = userSocketSessionMap.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry<String,WebSocketSession> entry = iterator.next();
if(entry.getValue().getAttributes().get("uid")==webSocketSession.getAttributes().get("uid")){
userSocketSessionMap.remove(webSocketSession.getAttributes().get("uid"));
System.out.println("WebSocket in staticMap:" + webSocketSession.getAttributes().get("uid") + "removed");
@Override
public boolean supportsPartialMessages() {
return false;
//发送信息的实现
public void sendMessageToUser(String uid, TextMessage message) throws IOException {
WebSocketSession session = userSocketSessionMap.get(uid);
if (session != null && session.isOpen()) {
session.sendMessage(message);
定义 HandShakeInterceptor 握手拦截器
package com.mlkj.common.websocket;
import com.mlkj.common.utils.StringUtils;
import com.mlkj.modules.sys.utils.UserUtils;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;
import javax.servlet.http.HttpSession;
import java.util.Map;
* websocket握手拦截器
* 拦截握手前,握手后的两个切面
public class HandShakeInterceptor implements HandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
System.out.println("Websocket:用户[ID:" + ((ServletServerHttpRequest) serverHttpRequest).getServletRequest().getSession(false).getAttribute("id") + "]已经建立连接");
if (serverHttpRequest instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) serverHttpRequest;
HttpSession session = servletRequest.getServletRequest().getSession(false);
// 标记用户
String id = UserUtils.getUser().getLoginName();
if (StringUtils.isNotBlank(id)) {
map.put("uid", id);//为服务器创建WebSocketSession做准备
System.out.println("用户id:" + id + " 被加入");
} else {
System.out.println("user为空");
return false;
return true;
@Override
public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {
每次用户请求的时候通过后台获取到用户的id,将id放入到map中,带到WebSocketHandler.userSocketSessionMap 中存储起来
前端代码:
<script type="text/javascript">
var webSocket;
$(function() {
websocketRefresh()
function websocketRefresh(){
if('WebSocket' in window) {
var ip = '${pageContext.request.localAddr}';
var reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/
if( reg.test(ip)){
console.log("此浏览器支持websocket");
webSocket = new WebSocket("ws://"+ip+":${pageContext.request.localPort}${ctx}/ws");
}else{
/*alert("ip地址"+ip+"不合法,请勿使用127.0.0.1或者http://localhost");*/
}catch (e) {
alert("websocket链接异常");
} else if('MozWebSocket' in window) {
alert("此浏览器只支持MozWebSocket");
} else {
alert("此浏览器只支持SockJS");
webSocket.onopen = function(event){
console.log("连接成功");
console.log(event);
webSocket.onerror = function(event){
console.log("连接失败");
console.log(event);
webSocket.onclose = function(event){
console.log("Socket连接断开");
console.log(event);
webSocket.onmessage = function(event){
//接受来自服务器的消息
//...
console.log(webSocket.onmessage);
console.log(event);
var actLsit = JSON.parse(event.data);
console.log(actLsit);
apendTask(actLsit.body.data.list);
/*websocket.onopen = function(evnt) {
console.log("链接服务器成功")
websocket.onmessage = function(evnt) {
var actLsit = JSON.parse(evnt.data);
console.log(actLsit.body.data);
apendTask(actLsit.body.data);
websocket.onerror = function(evnt) {};
websocket.onclose = function(evnt) {
if(i<10){
console.log("与服务器断开了链接,尝试连接"+i);
websocketRefresh();
}else{
alert("与服务器断开连接,请刷新活重新登录");
function apendTask(acts){
$("#act").empty();
console.log(acts);
var actsLength = acts.length;
if(actsLength>10){
actsLength =10;
var st = "";
for (var i = 0; i <actsLength ; i++) {
var vars = acts[i].vars;
st += '<div class="search-result search-hover" style="padding: 0 20px;">';
st += ' <div class="row">';
st += ' <div class="col-sm-5">';
st += ' <h4 style="margin: 10px 0;line-height: normal;">';
st += ' <a href="${ctx}/act/task/form?taskId=' + acts[i].taskId + '&taskName=' + acts[i].taskName + '&taskDefKey=' + acts[i].taskDefKey + '&procInsId=' + acts[i].procInsId + '&procDefId=' + acts[i].procDefId + '&status=' + acts[i].status + '">';
st += vars.map.title == null ? acts[i].taskId : vars.map.title;
st += ' </a>';
st += ' </h4>';
st += ' </div>';
st += ' <div class="col-sm-2 text-right" style="text-align: center;">';
st += ' <p class="forum-sub-title" style="font-size: 14px;margin: 10px 0;color: #181818;">';
st += acts[i].taskName;
st += ' </p>';
st += ' </div>';
st += ' <div class="col-sm-5" style="text-align: right;margin: 10px 0; color: #8b9298;">';
st += ' <div class="forum-sub-title">接收时间:' + acts[i].taskCreateDate + '</div>';
st += ' </div>';
st += ' </div>';
st += '</div>';
$("#act").append(st);
</script>
我的业务逻辑为每隔20秒从后台查询数据并推送到前端,具体的推送环节我使用了定时器 如下代码
package com.mlkj.common.schedule;
import com.google.gson.GsonBuilder;
import com.mlkj.common.json.AjaxJson;
import com.mlkj.common.persistence.Page;
import com.mlkj.modules.act.entity.Act;
import com.mlkj.modules.act.service.ActTaskService;
import com.mlkj.common.websocket.WebSocketHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.Map;
* 待办任务定时器
@Service
@Lazy(false)
public class actTaskTodoSchedule {
@Autowired
private WebSocketHandler handler;
@Autowired
private ActTaskService actTaskService;
* 每隔20秒获取所有连接websocket的用户,推送当前最新的代办列表
@Scheduled(cron = "*/20 * * * * ? ")
public void act() {
Map<String, WebSocketSession> stringWebSocketSessionMap = WebSocketHandler.userSocketSessionMap;
for (String st : stringWebSocketSessionMap.keySet()) {
try {
Page<Act> list = actTaskService.todoList(new Act(), st, new Page<Act>(0, 8));
AjaxJson ajaxJson = new AjaxJson();
ajaxJson.put("data",list);
handler.sendMessageToUser(st, new TextMessage(ajaxJson.getJsonStr()));
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
本人小白,刚刚接触,如有不对敬请指正
参考代码 https://blog.csdn.net/zmx729618/article/details/78584633
最近遇到一个需求,首页的待办任务要求实时刷新。刚开始的时候再前端写了一个定时器轮训查询。但是过了一段时间之后觉得太LOW逼了,正好想到了websocket,准备试验一下,废话不说,上代码:(注:这里使用maven方式添加 手动添加的同学请自行下载相应jar包放到lib目录,本文使用的版本为4.3.5.RELEASE) <!-- spring websocket-->...
将sql/schema.sql导入本地mysql,修改resources/system-config.properties中mysql用户名与密码。
使用maven jetty插件运行本项目,配置如下
Enjoy!
随缘链接(可能遭遇服务器到期崩溃等不可抗因素)
Email:
一般前端请求服务器接口都是使用主动请求的模式,有ajax等。但分不同使用场景 有不一样的需求, 比如要求实时性比较高的场景使用ajax则不好实现,且实现了开销也大。所以有了websocket这种长链接的形式保持在前端与服务器的通信协议。
现在很多后端框架都使用springboot,那么如何在springboot中整合websocket服务呢?直接上代码,更加清晰明了!
首先 在 ...
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<dependency>
1.场景需求后台攻城狮和前端攻城狮一起开发时,经常受到前端攻城狮的骚扰,动不动就来一句,那谁,帮我看一下接口访问出什么错了。。。我刚刚上传的参数过来了吗。。。你返回的是什么。。。我请求过去了吗。。。
好吧,就是这样的一种情况,然后我希望让他们自己去看后台日志,而又不想给他们登陆服务器的权限TAT。那就想办法把访问日志实时输出到web页面,这样他们打开页面就可以了。2.特别鸣谢1)特别感谢http:
*File Name: HandShake.java
*Package Name: com.yun.websocket
*Date: 2016年9月3日 下午4:44:27