Spring Boot 中整合WebSocket

引入依赖

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-websocket</artifactId>

</dependency>

配置文件

package cn.jt.config;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.socket.server.standard.ServerEndpointExporter;

*/

@Configuration

publicclass WebSocketConfig {

/**

* 初始化Bean,它会自动注册使用了 @ServerEndpoint 注解声明的 WebSocket endpoint

*

* @return

*/

@Bean

public ServerEndpointExporter serverEndpointExporter() {

returnnew ServerEndpointExporter();

}

}

在项目中的使用

复制代码
package com.unistrong.monitor.socket;

import com.alibaba.fastjson.JSONObject;
import com.unistrong.common.utils.JsonUtil;
import com.unistrong.common.utils.SpringUtils;
import com.unistrong.monitor.constant.RedisConstant;
import com.unistrong.monitor.service.RawNavService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

@ServerEndpoint("/socket/rawNav/Field")
@Slf4j
@Component
public class RawNavFieldWebSocket {

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

    /**
     * concurrent 包的线程安全Set,用来存放每个客户端对应的 Socket对象
     */
    public static ConcurrentHashMap<Session, Map<String, Object>> webSocketMap = new ConcurrentHashMap<>();

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

    /**
     * 连接建立成功调用的方法
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        this.session = session;
        addOnlineCount(); // 在线数 +1
        log.info("有新窗口开始监听,当前在线人数为{}", getOnlineCount());

    }

    /**
     * 关闭连接
     */

    @OnClose
    public void onClose() {
        subOnlineCount(); // 人数 -1
        webSocketMap.remove(session);
        log.info("有一连接关闭,当前在线人数为:{}", getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @param session
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info("收到来自窗口的信息:{}", message);

        if (StringUtils.isNotBlank(message)) {
            Map<String, Object> map = JSONObject.parseObject(message, Map.class);
            //获取前端传递的参数
            String stationCode = MapUtils.getString(map, "stationCode");
            String mode = MapUtils.getString(map, "mode");
            String equ = MapUtils.getString(map, "equ");
            String navSystem = MapUtils.getString(map, "navSystem");
            String navFrequency = MapUtils.getString(map, "navFrequency");
            String serviceType = MapUtils.getString(map, "serviceType");
            String prn = MapUtils.getString(map, "prn");


            RawNavService service = SpringUtils.getBean(RawNavService.class);
            Map<String, Object> initData = null;
            String commRecordKey = null;

            switch (serviceType) {
                case "rawnav_clk_err":
                    commRecordKey = String.format(RedisConstant.KEY_RAWNAV_COMM + RedisConstant.KEY_RAWNAV_CLKERR
                            + "%s:%s:%s:%s:%s", serviceType, mode, navSystem, navFrequency, prn);
                    initData = service.getInitDataClkErrField(commRecordKey);
                    break;
                case "rawnav_ephemeris":
                    commRecordKey = String.format(RedisConstant.KEY_RAWNAV_COMM + RedisConstant.KEY_RAWNAV_EPHEMERIS
                            + "%s:%s:%s:%s:%s", serviceType, mode, navSystem, navFrequency, prn);
                    initData = service.getInitDataEphemerisField(commRecordKey);
                    break;
                case "rawnav_iono":
                    commRecordKey = String.format(RedisConstant.KEY_RAWNAV_COMM + RedisConstant.KEY_RAWNAV_INNO
                            + "%s:%s:%s:%s:%s", serviceType, mode, navSystem, navFrequency, prn);
                    initData = service.getInitDataIonoField(commRecordKey);
                    break;
                case "rawnav_utc_param":
                    commRecordKey = String.format(RedisConstant.KEY_RAWNAV_COMM + RedisConstant.KEY_RAWNAV_TIMESYNCPARAM
                            + "%s:%s:%s:%s:%s", serviceType, mode, navSystem, navFrequency, prn);
                    initData = service.getInitDataUtcParamField(commRecordKey);
                    break;
                case "rawnav_almanace":
                    commRecordKey = String.format(RedisConstant.KEY_RAWNAV_COMM + RedisConstant.KEY_RAWNAV_ALMANACE
                            + "%s:%s:%s:%s:%s", serviceType, mode, navSystem, navFrequency, prn);
                    initData = service.getInitDataAlmanaceField(commRecordKey);
                    break;
            }

            //构建redisKey
            map.put("commRecordKey", commRecordKey);

            if (MapUtils.isNotEmpty(initData)) {
                sendMessage(session, JsonUtil.toJson(initData));

                webSocketMap.put(session, map);
            }
        }

    }


    /**
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("Error:{}", session.getId(), error.getMessage());
        error.printStackTrace();
    }

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


    public static void sendMessage(Session session, String message) {
        try {
            session.getBasicRemote().sendText(message);
            log.info(message);
        } catch (IOException e) {
            log.error("ServerDataWebSocket.消息发送失败,失败原因:{}", e.getMessage());
        }
    }


    private static synchronized int getOnlineCount() {
        return onlineCount;
    }

    private static synchronized void addOnlineCount() {
        RawNavFieldWebSocket.onlineCount++;
    }

    private static synchronized void subOnlineCount() {
        RawNavFieldWebSocket.onlineCount--;
    }


}

1、代码很简单,大家一看就知道逻辑了,这里就解释一下各个注解的含义

  • @ServerEndpoint: 将目前的类定义成一个websocket服务器端,注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端

  • @OnOpen: 当WebSocket建立连接成功后会触发这个注解修饰的方法。

  • @OnClose: 当WebSocket建立的连接断开后会触发这个注解修饰的方法。

  • @OnMessage: 当客户端发送消息到服务端时,会触发这个注解修改的方法。

  • @OnError: 当WebSocket建立连接时出现异常会触发这个注解修饰的方法。

  • 若无法自动注入,可使用

  • public class SpringUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

    if (SpringUtil.applicationContext == null) {

    SpringUtil.applicationContext = applicationContext; // 设置全局应用上下文

    }

    }

    // 获取应用上下文

    public static ApplicationContext getApplicationContext() {

    return applicationContext;

    }

    // 通过Bean名称获取Bean实例

    public static Object getBean(String name) {

    return getApplicationContext().getBean(name);

    }

    // 通过Bean类型获取Bean实例

    public static <T> T getBean(Class<T> clazz) {

    return getApplicationContext().getBean(clazz);

    }

    // 通过Bean名称和类型获取Bean实例

    public static <T> T getBean(String name, Class<T> clazz) {

    return getApplicationContext().getBean(name, clazz);

    }

    }

    该类在Spring启动时自动注入ApplicationContext(通过setApplicationContext方法),后续所有getBean调用都基于此上下文2

SpringBoot 开启 WebSocket

在启动类加上 @EnableWebSocket

相关推荐
fly-phantomWing12 小时前
Maven的安装与配置的详细步骤
java·后端·maven·intellij-idea
学编程的小鬼15 小时前
SpringBoot 自动装配原理剖析
java·spring boot·后端
码事漫谈15 小时前
SIMD编程入门:让性能飞起来的实践指南
后端
码事漫谈16 小时前
从汇编角度看C++优化:编译器真正做了什么
后端
老葱头蒸鸡17 小时前
(28)ASP.NET Core8.0 SOLID原则
后端·asp.net
老华带你飞17 小时前
机电公司管理小程序|基于微信小程序的机电公司管理小程序设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·微信小程序·小程序·机电公司管理小程序
拾忆,想起18 小时前
AMQP协议深度解析:消息队列背后的通信魔法
java·开发语言·spring boot·后端·spring cloud
PH = 718 小时前
Spring Ai Alibaba开发指南
java·后端·spring
不会吃萝卜的兔子18 小时前
springboot websocket 原理
spring boot·后端·websocket
Fency咖啡19 小时前
Spring Boot 内置日志框架 Logback - 以及 lombok 介绍
spring boot·后端·logback