踩坑篇之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…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent
安全大模型训练计划:基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标:为安全大模型创建高质量、去偏、符合伦理的训练数据集,涵盖安全相关任务(如有害内容检测、隐私保护、道德推理等)。 1.1 数据收集 描…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
