目录

  • 前言
  • pom的依赖
  • 编写WebSocketConfig配置类
  • 编写WebSocketServerController配置类
  • 前端初始化查询用户创建数
  •  测试    

项目目录结构

前言

前段时间公司做一个代办收件功能,后来要求在菜单右上角显示有多少笔代办件,由于时间充忙就直接查询数据库了,没有实时想前端推送新增的办件。所以在休息的时候来做这件事,你本文写的demo是查询当前用户的创建数量,大家一定会想到websocket;什么是websocket呢?这里就不一一介绍了,放入一个图给大家看,可以自行查找看下原理。

è¿éåå¾çæè¿°

POM的依赖

SpringBoot2.0对WebSocket的支持简直非常好,直接就有包可以引入。

        <!-- websocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

编写WebSocketConfig配置类

package com.example.demo.config;

import com.example.demo.controller.WebSocketServerController;
import com.example.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

/**
 * @author LST
 * @version 1.0
 * @Description: 开启WebSocket支持
 * @date 2019-12-21 16:15
 */
@Configuration
public class WebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter(){
        return new ServerEndpointExporter();
    }

    /**
     * (第二种方法)因 SpringBoot WebSocket 对每个客户端连接都会创建一个         
      WebSocketServer(@ServerEndpoint 注解对应的) 对象,Bean 注入操作会被直接略过,因而手动 
       注入一个全局变量
     * @param userMapper
     */
    @Autowired
    public void setUserMapper(UserMapper userMapper){
        WebSocketServerController.userMapper = userMapper;
    }

}

编写WebSocketServerController配置类

因为WebSocket是类似客户端服务端的形式(采用ws协议),那么这里的WebSocketServer其实就相当于一个ws协议的Controller
直接@ServerEndpoint("/websocket")@Component启用即可,然后在里面实现@OnOpen,@onClose,@onMessage等方法。

package com.example.demo.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.service.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @author LST
 * @version 1.0
 * @Description: websocker接口
 *              实现查询本人创建的用户数
 * @date 2019-12-21 16:52
 */
@ServerEndpoint("/websocket/{uid}")
@Component
@Slf4j
public class WebSocketServerController {

    //此处是解决无法注入的关键
    private static ApplicationContext applicationContext;

    public static  UserMapper userMapper;

    /**
     * 第一种方法(在主入口中注入applicationContext)
     * @param applicationContext
     *//*
    public static void setApplicationContext(ApplicationContext applicationContext) {
        WebSocketServerController.applicationContext = applicationContext;
    }*/


    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;

    /**
     * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
     */
    private static CopyOnWriteArraySet<WebSocketServerController> webSocketSet = new CopyOnWriteArraySet<WebSocketServerController>();

    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    private Session session;

    //接收sid
    private String sid = "";

    /**
     * 连接建立成功调用的方法
     * @param session 与某个客户端的连接会话,需要通过它来给客户端发送数据
     * @param uid 用户ID
     */
    @OnOpen
    public void onOpen(Session session,@PathParam("uid") String uid) {
        this.session = session;
        webSocketSet.add(this);     //加入set中
        //查找用户是UID创建的个数
        QueryWrapper wrapper = new QueryWrapper();
        wrapper.like("create_by", uid);
        /*userMapper = SpringContextHolder.getBean(UserMapper.class);*/
        List<User> userList = userMapper.selectList(wrapper);
        //查询
        log.info("用户:{},创建的人数为:{}",uid,userList.size()<=0?0:userList.size());
        this.sid = uid;
        try {
            sendMessage(String.valueOf(userList.size()<=0?0:userList.size()));
        } catch (IOException e) {
            log.error("websocket IO异常");
        }
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  //从set中删除
       /* log.info("有一连接关闭!当前在线人数为" + getOnlineCount());*/
    }

    /**
     * 收到客户端消息后调用的方法
     * @param message 客户端发送过来的消息
     * @param session 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到来自窗口"+sid+"的信息:"+ message);
        //群发消息
        for (WebSocketServerController item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发生错误
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("发生错误");
        error.printStackTrace();
    }

    /**
     * 实现服务器主动推送
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }


    /**
     * 群发自定义消息
     * @param message
     * @param uid
     * @throws IOException
     */
    public static void sendInfo(String message,@PathParam("uid") String uid) throws IOException {
        log.info("推送消息到窗口"+uid+",推送内容:"+message);
        for (WebSocketServerController item : webSocketSet) {
            try {
                //这里可以设定只推送给这个sid的,为null则全部推送
                if(uid==null) {
                    item.sendMessage(message);
                }else if(item.sid.equals(uid)){
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                continue;
            }
        }
    }

   /* public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocketServerController.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocketServerController.onlineCount--;
    }*/

}

这里有一个问题,就是集成websocket想用自己写的server时用@Autowired注入会失败,这里我用两种办法解决,任选其一。

第一种就是在WebSocketConfig 配置手动注入。

第二种是在(在主入口中注入applicationContext)。

前端初始化查询用户创建数

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>socket</title>
    <script src="/static/js/jquery.min.js"></script>
    <script type="text/javascript">
        var socket;
        if(typeof(WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
        }else{
            console.log("您的浏览器支持WebSocket");
            //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
            //等同于socket = new WebSocket("ws://localhost:8083/checkcentersys/websocket/20");
            socket = new WebSocket("http://127.0.0.1:8090/websocket/543edaa433ee4d94813fd0ec7527f3ea".replace("http","ws"));
            //打开事件
            socket.onopen = function() {
                console.log("Socket 已打开");
                //socket.send("这是来自客户端的消息" + location.href + new Date());
            };
            //获得消息事件
            socket.onmessage = function(msg) {
                console.log("服务端链接成功,返回信息"+msg.data)
                //发现消息进入    开始处理前端触发逻辑
                $('#userNum').val(msg.data);
            };
            //关闭事件
            socket.onclose = function() {
                console.log("Socket已关闭");
            };
            //发生了错误事件
            socket.onerror = function() {
                alert("Socket发生了错误");
                //此时可以尝试刷新页面
            }
            //离开页面时,关闭socket
            //jquery1.8中已经被废弃,3.0中已经移除
            // $(window).unload(function(){
            //     socket.close();
            //});
        }
    </script>
    
    <script type="text/javascript">
        function ajaxSend() {
            var url = "http://127.0.0.1:8090/checkcenter/socket/push/";
            $.ajax({
                url: url+"543edaa433ee4d94813fd0ec7527f3ea",
                type: 'GET',
                dataType: 'JSON',
                success: function (data) {
                    console.log(data)
                    if(data.data != ''){
                       alert("创建成功"+data.data);
                    }
                },
                error: function (msg) {

                }
            });
        }
        
    </script>

</head>


<body>
  <div>
      创建的人数:<input type="text" id="userNum">

  </div>
  <div>
      <span onclick="ajaxSend('543edaa433ee4d94813fd0ec7527f3ea')" style="color: rgba(20,146,255,1);">创建用户</span>
  </div>


</body>
</html>

后端创建用户推送消息给指定用户

package com.example.demo.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.entity.User;
import com.example.demo.mapper.UserMapper;
import com.example.demo.result.RestResponse;
import com.example.demo.result.ResultGenerator;
import com.example.demo.utils.IdGenerate;
import io.swagger.annotations.Api;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;
import java.util.Date;
import java.util.List;

/**
 * @author LST
 * @version 1.0
 * @Description: 消息推送
 * @date 2019-12-21 19:11
 */
@RestController
@RequestMapping("/checkcenter")
@Api(value = "CheckCenterController",  tags = "socket")
public class CheckCenterController {

    @Autowired
    private UserMapper userMapper;

    /**
     * 用户创建和推送数据接口
     * @param uid 用户ID
     * @return
     */
    @ResponseBody
    @RequestMapping("/socket/push/{uid}")
    public RestResponse pushToWeb(@PathVariable String uid) {
        String message ="";
        try {
            User user = new User();
            user.setCreateBy(uid);
            user.setCreateDate(new Date());
            user.setUpdateBy(uid);
            user.setUpdateDate(new Date());
            user.setDelFlag("0");
            user.setId(IdGenerate.generateId());
            if(userMapper.insert(user) > 0){
                //查找用户是UID创建的个数
                QueryWrapper wrapper = new QueryWrapper();
                wrapper.like("create_by", uid);
                List<User> userList = userMapper.selectList(wrapper);
                message = String.valueOf(userList.size()<=0?0:userList.size());
            }
            WebSocketServerController.sendInfo(message,uid);
        } catch (IOException e) {
            e.printStackTrace();
            return ResultGenerator.genFailResult(uid+"#"+e.getMessage());
        }
        return ResultGenerator.genSuccessResult(uid+":"+message);
    }

}

测试

1、将项目启动后访问http://127.0.0.1:8090/socket

2、点击创建用户按钮创建用户,默认是当前用户,用户数据也是模拟的。消息会通过sendInfo发到指定的用户(这个用户已经创建了websocket通信)。

前端显示如图

 

需要源码的伙伴可前往自行下载,附上下载地址:https://download.csdn.net/download/qq_33612228/12046066

 

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐