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

相关推荐
板板正39 分钟前
SpringAI——向量存储(vector store)
java·spring boot·ai
野生技术架构师1 小时前
Spring Boot 定时任务与 xxl-job 灵活切换方案
java·spring boot·后端
苹果醋32 小时前
Java并发编程-Java内存模型(JMM)
java·运维·spring boot·mysql·nginx
寒士obj3 小时前
SpringBoot中的条件注解
java·spring boot·后端
G探险者3 小时前
循环中的阻塞风险与异步线程解法
后端
易元3 小时前
模式组合应用-桥接模式(二)
后端
三婶儿3 小时前
在没有客户端的客户环境下,如何用 Python 一键执行 MySQL 与达梦数据库 SQL
运维·后端·python
vvilkim4 小时前
深入理解 Spring Boot Starter:简化依赖管理与自动配置的利器
java·前端·spring boot
G探险者4 小时前
Java 线程相关的三个常见接口、类
后端
学历真的很重要4 小时前
Eino 开源框架全景解析 - 以“大模型应用的搭积木指南”方式理解(一)
后端·语言模型·面试·golang·ai编程·eino