相关文章推荐
酷酷的煎鸡蛋  ·  Golang WebSocket Ping ...·  1 月前    · 
飘逸的荔枝  ·  WebSockets support in ...·  1 月前    · 
豪爽的大海  ·  SpringBoot ...·  8 月前    · 
腹黑的香瓜  ·  Azure OpenAI 入门教程 - ...·  10 月前    · 

最近遇到一个需求,首页的待办任务要求实时刷新。刚开始的时候再前端写了一个定时器轮训查询。但是过了一段时间之后觉得太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) &lt;!-- spring websocket--&gt;... 将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