前排提醒:还是博主菜,见识短浅,没遇到过这个问题。。。
起因
前段时间学习websocket和sse,写demo用了spring框架。后来又写了新的spring单元测试类demo去测试,结果启动后报错,报错信息提示websocket的相关错误,大概意思时由于测试环境缺少WebSocket相关配置或依赖所导致。
报错信息
XMLjava.lang.IllegalStateException: Failed to load ApplicationContext at 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 available at 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 available at 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
这是博主用的很简单的一个测试类:
java
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 {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Test
public 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 环境),并随机选择一个可用端口。

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