踩坑篇之WebSocket实现类中无法使用@Autowired注入对象
大家好,我是小简,今天我又大意了,在
WebSocket这个类上踩坑了。

接下来我讲讲我踩坑的经历吧!
package cn.donglifeng.shop.socket.endpoin;import cn.donglifeng.shop.common.context.SpringBeanContext;
import cn.donglifeng.shop.common.redis.RedisUtil;
import cn.donglifeng.shop.socket.config.WebSocketConfiguration;
import cn.donglifeng.shop.socket.util.WebSocketEndpointTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;import javax.annotation.Resource;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.concurrent.atomic.AtomicInteger;/*** @author JanYork* @date 2023/3/14 11:36* @description WebSocket服务端点*/
@ServerEndpoint(value = "/websocket/{uid}",configurator = WebSocketConfiguration.class)
@Component
@Slf4j
public class WebSocketEndpoint {@Resourcepublic RedisUtil redisUtil;/*** 连接建立成功调用的方法** @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据* @param uid 用户id*/@OnOpenpublic void onOpen(Session session, @PathParam("uid") String uid) {try {redisUtil.socketOnline(Long.parseLong(uid));} catch (Exception e) {e.printStackTrace();}}/*** 收到客户端消息后调用的方法*/@OnMessagepublic void onMessage(String message) {if (StringUtils.hasLength(message)) {//TODO 业务逻辑} else {}}/*** 连接错误调用的方法** @param error 错误信息*/@OnErrorpublic void onError(Throwable error) {error.printStackTrace();}/*** 连接关闭调用的方法** @param session 会话* @param uid 用户id*/@OnClosepublic void onClose(Session session, @PathParam("uid") String uid) {}/*** @return 在线人数*/public AtomicInteger getOnlineCount() {return new AtomicInteger(redisUtil.countSocketOnline().intValue());}
}
上面是一个很简单的WebSocket端点服务类。
我打算使用Redis的Bitmap来做连接人数统计。
空指针?
@Resource
public RedisUtil redisUtil;
我直接注入我封装的Redis工具类,然后自信满满的开始测试。
结果…

???
居然空指针???什么情况?
我是百思难得其解呀,因为这个类本身也是一个Bean,使用了@Component注解。
寻找答案
我开始使用万能的浏览器搜索。
于是在一番搜寻后,在CSDN东拼西凑,综合找到以下答案:
首先,使用了@ServerEndpoint注解的类中使用@Resource或@Autowired注入都会失败,并且报出空指针异常。
原因是WebSocket服务是线程安全的,那么当我们去发起一个ws连接时,就会创建一个端点对象。
那么问题就在这了,根据CSDN上的说明,WebSocket服务是多对象的,不是单例的。
而我们的Spring的Bean默认就是单例的,在非单例类中注入一个单例的Bean是冲突的。
而且我虽然使用@Component注解了这个类,但是WebSocket的端点仍然不是单例的,这个是必须的,端点服务不可能单例。
来自
CSDN:
@Autowired注解注入对象是在启动的时候就把对象注入,而不是在使用A对象时才把A需要的B对象注入到A中。
而WebSocket在刚刚有说到,有连接时才实例化对象,而且有多个连接就有多个。
如何解决?
知道原因还不好解决吗?我们开发的适合,基本上很常见的遇到要在非Bean的类中使用Bean,因为不被Spring容器所管理的类中是无法注入Bean对象的,所以我们需要去使用一个上下文类,在一开始就将Spring中所有的Bean静态化到上下文类中。
如何实现?
定义一个类,实现ApplicationContextAware接口:
public class SpringBeanContext implements ApplicationContextAware
不过需要注意的是!这个类也必须要是Bean,不如无法获取到Spring的ApplicationContext。
@Component
public class SpringBeanContext implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {context = applicationContext;}
}
重写他的setApplicationContext方法,将ApplicationContext赋值给本类静态的属性。
此时,当我们启动程序,Spring中的Bean对象就全部会被context获取到。
然后我们还需要写从上下文中获取Bean的方法,我就直接丢代码了:
package cn.donglifeng.shop.common.context;import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;/*** @author JanYork* @date 2023/3/8 9:33* @description SpringBean上下文*/
@Component
public class SpringBeanContext implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {context = applicationContext;}/*** 获取上下文** @return 上下文对象*/public static ApplicationContext getContext() {return context;}/*** 根据beanName获取bean** @param beanName bean名称* @return bean对象*/public Object getBean(String beanName) {return context.getBean(beanName);}/*** 根据beanName和类型获取bean** @param beanName bean名称* @param clazz bean类型* @param <T> bean类型* @return bean对象*/public <T> T getBean(String beanName, Class<T> clazz) {return context.getBean(beanName, clazz);}/*** 根据类型获取bean** @param clazz bean类型* @param <T> bean类型* @return bean对象*/public <T> T getBean(Class<T> clazz) {return context.getBean(clazz);}
}
解决效果
/*** 连接建立成功调用的方法** @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据* @param uid 用户id*/@OnOpenpublic void onOpen(Session session, @PathParam("uid") String uid) {try {RedisUtil bean = SpringBeanContext.getContext().getBean(RedisUtil.class);bean.socketCache(uid, session);} catch (Exception e) {e.printStackTrace();}}
这里我通过上下文类去获取到Bean对象,然后测试连接成功了。
扩展知识
注意!我这里有坑,别踩着了,我测试的适合数据还是写入失败了,我这里是想将Socket的Session丢到Redis里面实现分布式环境对象共享(小小的尝试)。
bean.socketCache(uid, session);
显然是不行的,序列化会报错,因为:

看他的源码,他没有去实现Serializable接口,是不能被序列化的!
好了,此文结束,下一篇小简来讲将分布式环境下WebSocket的同步问题吧!
相关文章:
踩坑篇之WebSocket实现类中无法使用@Autowired注入对象
大家好,我是小简,今天我又大意了,在WebSocket这个类上踩坑了。 接下来我讲讲我踩坑的经历吧! package cn.donglifeng.shop.socket.endpoin;import cn.donglifeng.shop.common.context.SpringBeanContext; import cn.donglifeng.s…...
QT CTK插件框架 (一 下载编译)
CTK 为支持生物医学图像计算的公共开发包,其全称为 Common Toolkit。为医学成像提供一组统一的基本功能;促进代码和数据的交互及结合;避免重复开发;在工具包(医学成像)范围内不断扩展到新任务,而…...
【Java版oj】day10 井字棋、密码强度等级
目录 一、井字棋 (1)原题再现 (2)问题分析 (3)完整代码 二、密码强度等级 (1)原题再现 (2)问题分析 (3)完整代码 一、井字棋 &a…...
JavaScript的事件传播机制
你在学习和编写JavaScript时可能听说过事件冒泡(event bubbling)。它会发生在多个元素存在嵌套关系,并且这些元素都注册了同一事件(例如click)的监听器时。 但是事件冒泡只是事件机制的一部分。它经常与事件捕获(event capturing)和事件传播…...
队列的定义及基本操作实现(链式)
个人主页:【😊个人主页】 系列专栏:【❤️数据结构与算法】 学习名言:天子重英豪,文章教儿曹。万般皆下品,惟有读书高 系列文章目录 第一章 ❤️ 学前知识 第二章 ❤️ 单向链表 第三章 ❤️ 递归 文章目录…...
集成方法!
目录 关注降低variance,选择bias较小的基学习器 Bagging Stacking Random Forest 关注降低bias,选择variance较小的基学习器 Adaboost Boosting 关注降低variance,选择bias较小的基学习器 Bagging 给定m个样本的数据集,利用有放回的随机采样法,得…...
20年程序员生涯,读了200多本技术书,挑了几本精华好书分享给大家
不知不觉已经又走过了20个年头了,今年已经44了,虽然我已经退休在家,但一直都保持着读书的习惯,我每年平均要读10本技术书籍,保持不让自己的技术落伍。 这些年读的技术书不下200本,很多好书我都会保存在家&a…...
C++ 手写一个WebServer
文章目录 前言一、WebServer的原理刨析二、HTTP协议基础三、C++代码实战四、运行测试前言 本文由:我不会画饼呀 提供建议 大家如果有什么想看的文章(想了解的知识点),都可以在本专栏文章底部评论,或者私信我,在有能力的前提下,我都会尽量给大家写出来,供大家学习参考 …...
Elasticsearch 简介与安装
简介 Elasticsearch 是一个开源的搜索引擎,建立在一个全文搜索引擎库 Apache Lucene™ 基础之上。 Lucene 可以说是当下最先进、高性能、全功能的搜索引擎库—无论是开源还是私有。 但是 Lucene 仅仅只是一个库。为了充分发挥其功能,你需要使用 Java…...
Qt5.12实战之QByteArray与字符指针及字符串转换
示例源码:#include <QCoreApplication> #include <QDebug> #include <QTextStream> static QTextStream cout (stdout,QIODevice::WriteOnly); #include <iostream> #include <QtGlobal> #include <QByteArray>void test() {qDebug() <…...
二、ElasticSearch基础语法
目录一、简单了解ik分词器(分词效果)1.standard(单字分词器,es默认分词器)2.ik_smart分词(粗粒度的拆分)3.ik_max_word分词器(最细粒度拆分)二、指定默认分词器1.为索引指定默认分词器三、ES操作数据1.概述2.创建索引3.查询索引4.删除索引5.添…...
Yolov8详解与实战
文章目录摘要模型详解C2F模块Losshead部分模型实战训练COCO数据集下载数据集COCO转yolo格式数据集(适用V4,V5,V6,V7,V8)配置yolov8环境训练测试训练自定义数据集Labelme数据集摘要 YOLOv8 是 ultralytics …...
多线程案例——阻塞队列
目录 一、阻塞队列 1. 生产者消费者模型 (1)解耦合 (2)“削峰填谷” 2. 标准库中的阻塞队列 3. 自己实现一个阻塞队列(代码) 4. 自己实现生产者消费者模型(代码) 一、阻塞队列…...
学习优秀博文(【国产MCU移植】手把手教你使用RT-Thread制作GD32系列BSP)有感 | 文末赠书5本
学习优秀博文(【guo产MCU移植】手把手教你使用RT-Thread制作GD32系列BSP)有感 一篇优秀的博文是什么样的?它有什么规律可循吗?优秀的guo产32位单片机处理器是否真的能成功替换掉stm32的垄断地位? 本文博主以亲身经历聊…...
写用例写的焦头烂额?看看摸鱼5年的老点工是怎么写的...
给你个需求,你要怎么转变成最终的用例? 直接把需求文档翻译一下就完事了。 老点工拿到需求后的标准操作: 第一步:解析需求 先解析需求-找出所有需求中的动词,再列出所有测试点。测试点过程不断发散,对于…...
基于深度学习的鸟类检测识别系统(含UI界面,Python代码)
摘要:鸟类识别是深度学习和机器视觉领域的一个热门应用,本文详细介绍基于YOLOv5的鸟类检测识别系统,在介绍算法原理的同时,给出Python的实现代码以及PyQt的UI界面。在界面中可以选择各种鸟类图片、视频以及开启摄像头进行检测识别…...
零基础搭建Tomcat集群(超详细)
💗推荐阅读文章💗 🌸JavaSE系列🌸👉1️⃣《JavaSE系列教程》🌺MySQL系列🌺👉2️⃣《MySQL系列教程》🍀JavaWeb系列🍀👉3️⃣《JavaWeb系列教程》…...
机器学习自学笔记——聚类
聚类的基本概念 聚类,顾名思义,就是将一个数据集中各个样本点聚集成不同的“类”。每个类中的样本点都有某些相似的特征。比如图书馆中,会把成百上千的书分成不同的类别:科普书、漫画书、科幻书等等,方便人们查找。每…...
注意下C语言整形提升
C语言整形提升 C语言整形提升是指在表达式中使用多种类型的数据时,编译器会自动将较小的类型转换为较大的类型,以便进行运算。在C语言中,整型提升规则如下: 如果表达式中存在short类型,则将其自动转换为int类型。 如…...
Go panic的学习
一、前言 我们的应用程序常常会出现异常,包括由运行时检测到的异常或者应用开发者自己抛出的异常。 异常在一些其他语言中,如c、java,被叫做Exception,主要由抛出异常和捕获异常两部分组成。异常在go语言中,叫做pani…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
多模态大语言模型arxiv论文略读(108)
CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题:CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者:Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...
热烈祝贺埃文科技正式加入可信数据空间发展联盟
2025年4月29日,在福州举办的第八届数字中国建设峰会“可信数据空间分论坛”上,可信数据空间发展联盟正式宣告成立。国家数据局党组书记、局长刘烈宏出席并致辞,强调该联盟是推进全国一体化数据市场建设的关键抓手。 郑州埃文科技有限公司&am…...
云安全与网络安全:核心区别与协同作用解析
在数字化转型的浪潮中,云安全与网络安全作为信息安全的两大支柱,常被混淆但本质不同。本文将从概念、责任分工、技术手段、威胁类型等维度深入解析两者的差异,并探讨它们的协同作用。 一、核心区别 定义与范围 网络安全:聚焦于保…...
怎么开发一个网络协议模块(C语言框架)之(六) ——通用对象池总结(核心)
+---------------------------+ | operEntryTbl[] | ← 操作对象池 (对象数组) +---------------------------+ | 0 | 1 | 2 | ... | N-1 | +---------------------------+↓ 初始化时全部加入 +------------------------+ +-------------------------+ | …...
麒麟系统使用-进行.NET开发
文章目录 前言一、搭建dotnet环境1.获取相关资源2.配置dotnet 二、使用dotnet三、其他说明总结 前言 麒麟系统的内核是基于linux的,如果需要进行.NET开发,则需要安装特定的应用。由于NET Framework 是仅适用于 Windows 版本的 .NET,所以要进…...
qt+vs Generated File下的moc_和ui_文件丢失导致 error LNK2001
qt 5.9.7 vs2013 qt add-in 2.3.2 起因是添加一个新的控件类,直接把源文件拖进VS的项目里,然后VS卡住十秒,然后编译就报一堆 error LNK2001 一看项目的Generated Files下的moc_和ui_文件丢失了一部分,导致编译的时候找不到了。因…...
