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

相关推荐
晚风吹长发4 小时前
初步了解Linux中的动静态库及其制作和使用
linux·运维·服务器·数据结构·c++·后端·算法
梁下轻语的秋缘5 小时前
ESP32-WROOM-32E存储全解析:RAM/Flash/SD卡读写与速度对比
java·后端·spring
wanzhong23335 小时前
开发日记8-优化接口使其更规范
java·后端·springboot
J_liaty6 小时前
SpringBoot + EMQX:打造物联网设备数据双向通讯的完整解决方案
spring boot·物联网·emqx
羊小猪~~6 小时前
【QT】--文件操作
前端·数据库·c++·后端·qt·qt6.3
张彦峰ZYF7 小时前
商品供给域的工程化简要设计考量
后端·系统架构·商品模型·商品供给
Coder_Boy_8 小时前
基于SpringAI的在线考试系统-考试系统DDD(领域驱动设计)实现步骤详解
java·数据库·人工智能·spring boot
小北方城市网8 小时前
微服务注册中心与配置中心实战(Nacos 版):实现服务治理与配置统一
人工智能·后端·安全·职场和发展·wpf·restful
crossaspeed9 小时前
Java-SpringBoot的启动流程(八股)
java·spring boot·spring
这儿有个昵称9 小时前
互联网大厂Java面试场景:从Spring框架到微服务架构的提问解析
java·spring boot·微服务·kafka·grafana·prometheus·数据库优化