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

相关推荐
杨DaB1 小时前
【SpringMVC】拦截器,实现小型登录验证
java·开发语言·后端·servlet·mvc
努力的小雨8 小时前
还在为调试提示词头疼?一个案例教你轻松上手!
后端
魔都吴所谓8 小时前
【go】语言的匿名变量如何定义与使用
开发语言·后端·golang
陈佬昔没带相机8 小时前
围观前后端对接的 TypeScript 最佳实践,我们缺什么?
前端·后端·api
旋风菠萝9 小时前
JVM易混淆名称
java·jvm·数据库·spring boot·redis·面试
Livingbody10 小时前
大模型微调数据集加载和分析
后端
Livingbody10 小时前
第一次免费使用A800显卡80GB显存微调Ernie大模型
后端
weisian15110 小时前
Java WEB技术-序列化和反序列化认识(SpringBoot的Jackson序列化行为?如何打破序列化过程的驼峰规则?如何解决学序列化循环引用问题?)
java·spring boot
橘子编程10 小时前
SpringMVC核心原理与实战指南
java·spring boot·spring·tomcat·mybatis
Goboy11 小时前
Java 使用 FileOutputStream 写 Excel 文件不落盘?
后端·面试·架构