当前位置: 首页 > news >正文

springboot项目之websocket的坑:spring整合websocket后进行单元测试后报错的解决方案

前排提醒:还是博主菜,见识短浅,没遇到过这个问题。。。

起因

前段时间学习websocket和sse,写demo用了spring框架。后来又写了新的spring单元测试类demo去测试,结果启动后报错,报错信息提示websocket的相关错误,大概意思时由于测试环境缺少WebSocket相关配置或依赖所导致。

报错信息


java.lang.IllegalStateException: Failed to load ApplicationContextat org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:98)at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:248)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)at org.junit.runners.ParentRunner.run(ParentRunner.java:413)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serverEndpointExecutor' defined in class path resource [com/realpractice/demo/config/WebSocketConfiguration.class]: Invocation of init method failed; nested exception is java.lang.IllegalStateException: javax.websocket.server.ServerContainer not availableat org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1804)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:620)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542)at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:955)at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:918)at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:583)at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:745)at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:420)at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:144)at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:141)at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:90)... 27 more
Caused by: java.lang.IllegalStateException: javax.websocket.server.ServerContainer not availableat org.springframework.util.Assert.state(Assert.java:76)at org.springframework.web.socket.server.standard.ServerEndpointExporter.afterPropertiesSet(ServerEndpointExporter.java:107)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1863)at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1800)... 42 more

这是博主用的很简单的一个测试类:

import xxx.xxx.xxx.config.RedisConfig;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;import java.io.Serializable;@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class)
@ContextConfiguration(classes = RedisConfig.class) // 启用自定义配置
public class RedisTest {@Autowiredprivate RedisTemplate<String, String> redisTemplate;@Testpublic void test() {redisTemplate.opsForValue().set("test", "test");System.out.println(redisTemplate.opsForValue().get("test"));}
}

启动后报错:

刚开始有点懵,我都没测你websocket,你给我报个websocket的错。

学习一波。。。

根本原因是由于测试环境下没有websocket的环境,所以测试的时候需要注释掉它。并且测试中不选择让websocket加载有很多种方式,这个我们稍后再说。先来看看spring本身,既然报了websocket的错,说明spring本身去加载到了项目config目录下所有使用@Configuration注解的配置类。那么抛出我们第一个问题:为什么非 WebSocket 的测试类会加载 WebSocket 配置?

问题

为什么测试类会加载 WebSocket 配置?

这就不得不提到springboot的自动加载机制:

@SpringBootTest 会加载整个应用的上下文(包括所有 @Component, @Configuration, @Service 等)。

只要你的 WebSocketConfig 类被 @Configuration 或 @EnableWebSocket 标记,它就会被扫描并初始化。


那有的朋友又要问了,那我知道了springboot的自动加载机制,正常启动项目websocket也没错,为什么一使用测试就报错呢?不是说@SpringBootTest会加载整个应用的上下文吗?

嗯,博主看了下,原因好像是底层的依赖冲突,导致spring加载了错误的实现类。【详细原因的博主自己还没真正的测试过,就不妄下结论了。感兴趣的朋友直接浏览器搜索:“javax.websocket.server.ServerContainer not available”,跟着打波断点走下流程肯定能更明白整个逻辑】


多说一些关于sprngboot的相关知识把:

spring的组件扫描规则说白了就是"约定优于配置"的具体体现,即spring默认去加载所有可能的组件,减少显式配置。

还有一点是"上下文完整性":即spring确保测试环境和生产环境一致,避免因部分组件未加载导致测试结果不准确。

但也会带来一些副作用:测试启动变慢 和 依赖耦合,导致最终加载了不必要的组件,可能因它的初始化失败导致整个测试类失败。

解决

好好好,说了这么多怎么解决这个问题呢?

首先简单粗暴的办法就是在测试时把websocket相关代码全部注释掉,正常启动时再取消注释就可以了。(下下策)

我们可以把websocket的config写成根据不同配置选择是否加载,让其判断当前是否是测试环境,如果是则不加载websocket相关业务代码:

(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

通过注解参数 完全初始化 WebSocket 的基础设施,包括 ServerContainer。

明确指定了主配置类,并启动一个真实的嵌入式 web 服务器(而不仅仅是 mock 环境),并随机选择一个可用端口。

。。。总之是可以运行的。。。

内容还有待补充,有问题的朋友可以评论一起交流一下。文章如有错误劳驾读者朋友们指正,博主诚恳接受学习。谢谢大家。🌠

相关文章:

  • 网易大神安卓版游戏社交互动体验及功能评测
  • C++23/26 静态反射机制深度解析:编译时元编程的新纪元
  • 开源 Agent 框架对比:LangChain vs AutoGen vs CrewAI
  • 这是一款好用的PDF工具!
  • 《Q2门式起重机司机》考试大纲的专项要求有哪些?
  • Hadoop伪分布式模式搭建全攻略:从环境配置到实战测试
  • 使用多线程快速向Excel中快速插入一万条数据案例
  • 使用POI和EasyExcel使用导入
  • 湖北理元理律师事务所:债务管理领域的平台化创新探索
  • 学习记录:DAY19
  • 【虚幻5蓝图Editor Utility Widget:创建高效模型材质自动匹配和资产管理工具,从3DMax到Unreal和Unity引擎_系列第二篇】
  • 【数据可视化-41】15年NVDA, AAPL, MSFT, GOOGL AMZ股票数据集可视化分析
  • 【数据可视化-42】杂货库存数据集可视化分析
  • 数据结构 -- 图的应用(二)
  • MinecraftPVP发展史
  • TypeScript之type
  • 轻松上手:使用 Docker Compose 部署 TiDB 的简易指南
  • 多模态革命!拆解夸克AI相机技术架构:如何用视觉搜索重构信息交互?(附开源方案对比)
  • 6.进程概念(中)
  • TCP vs UDP:核心区别、握手过程与应用场景(附对比图)
  • 新一届中国女排亮相,奥运冠军龚翔宇担任队长
  • 黄仁勋访华期间表示希望继续与中国合作,贸促会回应
  • 香港警务处高级助理处长叶云龙升任警务处副处长(行动)
  • 牛市早报|今年国内核电项目审批首次开闸,离境退税起退点下调
  • 四川苍溪县教育局通报“工作人员辱骂举报学生”:停职检查
  • 马上评丨发钱奖励结婚,支持婚育就该系统性发力