springboot 优雅使用函数式编程处理 websocket @OnMessage 消息

背景

现在大多业务功能使用 socket.io实现长连接,但是部分第三方设备对接 只支持基础的websocket。
spring中使用基础的websocket, @OnMessage 收到消息,对消息的处理,if else 将会繁琐,难以维护。

本文仅介绍了如何使用enum枚举、java.util.function jdk8 函数式接口,实现消息的处理。

websocket 定义JSON 数据交换格式

本文使用的 示例格式:

//连接成功
{"cmd":"connect","sn":"A7888","data":{...}}
//设置人员
{"cmd":"setUser","data":{"userId":"1"}}
//控制设备 --多层级 的格式,第二层里面解析 仍可按照同样的方式来处理
{"cmd":"to_client","data":{"type":"openDoor","value":"ON"}}

springboot 集成websocket

pom.xml 依赖
        <!-- spring websocket--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency><!-- spring websocket启动异常、排除spring-boot-starter-tomcat--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId></exclusion></exclusions></dependency>       
定义WebSocketConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}
定义 @ServerEndpoint
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.OnOpen;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;/*** 定义websocket端点*/
@Slf4j
@ServerEndpoint(value = "/socket/device")
@Component
public class DeviceServerEndpoint {/*** 记录当前在线连接数*/private static AtomicInteger onlineCount = new AtomicInteger(0);/*** 连接的对象*/public static final Map<String, Session> clientMap = new ConcurrentHashMap<>();/*** 收到客户端消息** @param message 客户端发送过来的消息* @throws*/@OnMessagepublic void onMessage(String message, Session session) {try {DeviceMsg deviceMsg = JSON.parseObject(message, DeviceMsg.class);if (deviceMsg != null && deviceMsg.getCmd() != null) {// jdk8 函数式处理消息deviceMsg.getCmd().consumer.accept(session, message);} else {log.info("无法自动处理,客户端消息:{}", message);}} catch (Exception e) {log.error("消息处理失败", session.getId(), message);e.printStackTrace();}}/*** 连接建立成功*/@OnOpenpublic void onOpen(Session session) {onlineCount.incrementAndGet(); // 在线数加1log.info("有新连接加入:{},当前在线数为:{}", session.getId(), onlineCount.get());}/*** 连接关闭*/@OnClosepublic void onClose(Session session) {onlineCount.decrementAndGet(); // 在线数减1log.info("有一连接关闭:{},当前在线数为:{}", session.getId(), onlineCount.get());}@OnErrorpublic void onError(Session session, Throwable error) {onlineCount.decrementAndGet(); // 在线数减1error.printStackTrace();}/*** 测试 控制开锁*/public static void openDoor() {//所有客户端发送消息clientMap.forEach((id, session) -> {session.getBasicRemote().sendText("ON");}}
deviceMsg实体类
import lombok.Data;
import java.io.Serializable;@Data
public class DeviceMsg implements Serializable {/*** 指令*/Cmd cmd;/*** 数据块*/JSONObject data;
}
Cmd 核心消息处理 枚举类

import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import javax.websocket.Session;
import java.util.function.BiConsumer;@Getter
@Slf4j
@AllArgsConstructor
public enum Cmd {connect("设备连接成功", (Session session, String msg) -> {//设备端连接成功,发送设置端消息,将Session记录起来DeviceServerEndpoint.clientMap.put(session.getId(), session);}),ping("设备心跳", (Session session, String msg) -> {session.getBasicRemote().sendText("pong");}),setUser("配置用户", (Session session, String msg) -> {//拿到msg 转换对象或者其他操作session.getBasicRemote().sendText("ok");}),to_client("客户端消息", (Session session, String msg) -> {		try {String string = JSON.parseObject(msg).getJSONObject("data").getString("type");Client clientCmd = Client.valueOf(string);clientCmd.consumer.accept(session, msg);} catch (Exception e) {log.info("to_client客户端消息,无法自动处理:{}", msg);}});/*** 描述*/String desc;/*** 处理*/BiConsumer<Session, String> consumer;
}
Client 消息处理枚举类

import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;import javax.websocket.Session;
import java.util.function.BiConsumer;@Getter
@Slf4j
@AllArgsConstructor
public enum Client{openDoor("客户端控制", (Session session, String msg) -> {//测试 连接成功 直接 开门DeviceServerEndpoint.openDoor();}),otherCmd("其他指令", (Session session, String msg) -> {session.getBasicRemote().sendText("ok");});/*** 描述*/String desc;/*** 处理*/BiConsumer<Session, String> consumer;
}

JDK8常用函数式编程接口介绍:

  • Function<T, R>:接受一个类型为 T 的参数,返回类型为 R 的结果。常用方法包括 apply(T t)。
  • Predicate:接受一个类型为 T 的参数,返回一个布尔值。常用方法包括 test(T t)。
  • Consumer:接受一个类型为 T 的参数,没有返回值。常用方法包括 accept(T t)。
  • Supplier:不接受任何参数,返回一个类型为 T 的结果。常用方法包括 get()。
  • UnaryOperator:继承自 Function<T, R>,接受一个类型为 T 的参数,返回类型也为 T 的结果。常用方法包括 apply(T t)。
  • BinaryOperator:继承自 BiFunction<T, U, R>,接受两个类型为 T 的参数,返回类型也为 T 的结果。常用方法包括 apply(T t1, T t2)。
  • BiFunction<T, U, R>:接受两个参数,一个类型为 T,一个类型为 U,返回类型为 R 的结果。常用方法包括 apply(T t, U u)。
  • BiPredicate<T, U>:接受两个参数,一个类型为 T,一个类型为 U,返回一个布尔值。常用方法包括 test(T t, U u)。
  • BiConsumer<T, U> :用于接受两个参数,一个类型为 T,一个类型为 U,并且没有返回值。

函数式编程接口的引入,使得在 Java 中能够更方便地实现函数式编程的特性,如Lambda表达式和方法引用。它们可以用于各种场景,例如集合的处理、条件判断、函数的组合等。通过使用这些接口,可以编写更简洁、可读性更高的代码。

附:
WebSocket介绍

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/649871.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

yarn的安装

下载node.js&#xff0c;使用npm安装 #先加载本地安装 yarn install --offline//安装 npm install -g yarn yarn --version //查看版本 npm install -g yarn //安装安装node.js,下载yarn的安装程序: Yarn 淘宝源安装&#xff0c;分别复制粘贴以下代码行到黑窗口运行即可 …

考研机试 谁是你潜在的朋友

描述 “臭味相投”——这是我们描述朋友时喜欢用的词汇。两个人是朋友通常意味着他们存在着许多共同的兴趣。然而作为一个宅男&#xff0c;你发现自己与他人相互了解的机会并不太多。幸运的是&#xff0c;你意外得到了一份北大图书馆的图书借阅记录&#xff0c;于是你挑灯熬夜地…

Selenium wait element点击

场景&#xff1a;input单元格输入mail格式的字符串&#xff08;会进行字符串mail格式的校验&#xff09;&#xff0c;很快下方的button<next>才变绿可点击。 隐式等待&#xff1a;implicitly_wait method1: 点不上&#xff0c;这里有个坑&#xff0c;下面2种方式不能同时…

插槽(64-67)

文章目录 插槽1.插槽 - 默认插槽(组件内可以定制一处结构)2.插槽 - 后备内容&#xff08;默认值&#xff09;3.插槽 - 具名插槽(组件内可以定制多处结构)4.作用域插槽(插槽的一个传参语法) 插槽 插槽分类:默认插槽和具名插槽 1.插槽 - 默认插槽(组件内可以定制一处结构) 作用…

qt学习:http+访问百度智能云api实现动物图片识别

目录 获取id key 编程步骤 配置ui界面 添加模块,头文件和定义变量 新建两个类,一个图像Image类,一个Http类,http类继承QObject类,并添加头文件并定义成员和函数 实现图像Image类,Http类的函数 在ui界面的cpp中添加全局变量,根据自己的应用来写,开头有获取方法

Java解决ReabbitMQ解决消息重复消费问题

RabbitMQ中解决消息重复消费的问题通常涉及到确保消息的幂等性和使用消息确认机制。以下是一些常见的解决方法&#xff1a; 消息去重&#xff1a;在消费者端实现消息的去重逻辑&#xff0c;例如使用数据库的唯一约束或者分布式锁来保证同一消息不会被处理多次。 消息确认&…

【投稿优惠|EI优质会议】2024年材料化学与清洁能源国际学术会议(IACMCCE 2024)

【投稿优惠|优质会议】2024年材料化学与清洁能源国际学术会议(IACMCCE 2024) 2024 International Conference Environmental Engineering and Mechatronics Integration(ICEEMI 2024) 一、【会议简介】 随着全球能源需求的不断增长&#xff0c;清洁能源的研究与应用成为了国际…

【npm包】如何发布自己的npm包

随着Node.js的普及&#xff0c;npm&#xff08;Node Package Manager&#xff09;已成为JavaScript开发者中不可或缺的一部分。发布自己的npm包&#xff0c;不仅可以将自己的项目分享给更多人&#xff0c;还可以为社区做出贡献。本文将详细介绍如何从零开始发布自己的npm包。 …

【JavaEE Spring】MyBatis 操作数据库 - 进阶

MyBatis 操作数据库 - 进阶 1. 动态SQL1.1 \<if>标签1.2 \<trim>标签1.3 \<where>标签1.4 \<set>标签1.5 \<foreach>标签1.6 \<include>标签 1. 动态SQL 动态 SQL 是Mybatis的强⼤特性之⼀&#xff0c;能够完成不同条件下不同的 sql 拼接…

想找一个轻量版的MarkDown编辑器客户端,哪位推荐一下

经常需要即时写一些MarkDown文档&#xff0c;打开网页版的笔记不方便。 对比了几个&#xff0c;已收费的typora感觉还是最好的。 除此之外&#xff0c;原以为最重的VSCode&#xff0c;从打开速度、占内存等情况来说&#xff0c;居然也不相上下。 这样的对比条件下&#xff0c;…

某赛通电子文档安全管理系统 hiddenWatermark/uploadFile 文件上传漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

常用命令-

Linux服务器命令 scp -P 1122 expdp_aram_prod230903_* root193.8.1.7:/oracle/app/oracle/move_data //跨服务器传输 chown -R oracle:oinstall /app/move_data //更改所属组 date -s 2022-11-11 18:58:30 //修改服务器时间 top //CPU使用率 df -h //磁盘…

趋势也有大小之分?现货白银趋势的简单介绍

在现货白银市场中要做顺势交易&#xff0c;首先要分析趋势&#xff0c;在这一步很多投资者懵逼了&#xff0c;因为有时他们搞不清当前趋势是什么&#xff0c;看起来像下跌&#xff0c;但又像上涨。其实这可能是投资者没搞清楚大趋势和小趋势的关系问题&#xff0c;下面我们就来…

数据结构——链表的实现(Java版)

目录 一、链表 二、代码实现 1.创建结点 2.构造函数 3.链表的相关属性 4.添加虚拟节点 5.判断链表是否为空 6.添加方法 &#xff08;1&#xff09;在头部添加 &#xff08;2&#xff09;在尾部添加 &#xff08;3&#xff09;在索引位置添加 &#xff08;4&#xff…

华为云OBS-文件上传

前端配合后端 采用临时上传 相关参考文档 使用临时URL进行授权访问_对象存储服务 OBS_BrowserJS_临时授权访问_华为云 选择文件方法 【 isPay 是否上传完毕】 handleChange(file, fileList) {this.active 0;this.json_data [];console.log(file, fileList);fileList.forEa…

LiveGBS流媒体平台GB/T28181常见问题-如何配置使用自己已有的redis服务替换redis版本升级redis版本

LiveGBS如何配置使用自己已有的redis服务替换redis版本升级redis版本 1、Redis服务2、如何切换REDIS?2.1、停止启动REDIS2.2、配置信令服务2.3、配置流媒体服务2.4、启动 3、搭建GB28181视频直播平台 1、Redis服务 在LivGBS中Redis作为数据交换、数据订阅、数据发布的高速缓存…

Java二分查找-图文

一、二分查找概念 二分查找也叫折半查找&#xff0c;是在一组有序(升序/降序)的数据中查找一个元素&#xff0c;它是一种效率较高的查找方。 二、二分查找原理 1.二分查找的数组必须是有序数值型数组。 2.将想要查找的目标元素与查找范围内的中间元素进行比较&#xff0c;如果…

数据结构篇-01:单调栈

单调栈是栈的一种&#xff0c;可以使得每次新元素入栈后&#xff0c;栈内的元素都保持有序&#xff08;单调递增或者单调递减&#xff09;。 单调栈的用途不太广泛&#xff0c;只处理一类典型的问题&#xff0c;比如[下一个更大元素]、[上一个更小元素] 等。 在本文中&#x…

Pandas应用-股票分析实战

股票时间序列 时间序列&#xff1a; 金融领域最重要的数据类型之一 股价、汇率为常见的时间序列数据 趋势分析&#xff1a; 主要分析时间序列在某一方向上持续运动 在量化交易领域&#xff0c;我们通过统计手段对投资品的收益率进行时间序列建模&#xff0c;以此来预测未来的收…

六、VTK创建平面vtkPlaneSource

vtkPlaneSource创建位于平面中的四边形数组 先看看效果图: vtkPlaneSource 创建一个 m x n 个四边形数组,这些四边形在平面中排列为规则平铺。通过指定一个原点来定义平面,然后指定另外两个点,这两个点与原点一起定义平面的两个轴。这些轴不必是正交的 - 因此您可以创建平行…