踩坑篇之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…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
ip子接口配置及删除
配置永久生效的子接口,2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

mac 安装homebrew (nvm 及git)
mac 安装nvm 及git 万恶之源 mac 安装这些东西离不开Xcode。及homebrew 一、先说安装git步骤 通用: 方法一:使用 Homebrew 安装 Git(推荐) 步骤如下:打开终端(Terminal.app) 1.安装 Homebrew…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...

C++ 设计模式 《小明的奶茶加料风波》
👨🎓 模式名称:装饰器模式(Decorator Pattern) 👦 小明最近上线了校园奶茶配送功能,业务火爆,大家都在加料: 有的同学要加波霸 🟤,有的要加椰果…...

day36-多路IO复用
一、基本概念 (服务器多客户端模型) 定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标…...