1. JSR-356 容器握手失败
现象
- 控制台没有任何
afterConnectionEstablished
日志 - 客户端卡在握手阶段或报错超时
原因
- Spring MVC 默认用
StandardWebSocketClient()
,但未指定底层WebSocketContainer
- 嵌入式 Tomcat(JSR-356 实现)与默认容器不匹配,导致握手不发起
排查 & 解决
显式注入 JSR-356 容器:
1
2
3
4
5@Bean
fun webSocketClient(): WebSocketClient {
val container = ContainerProvider.getWebSocketContainer()
return StandardWebSocketClient(container)
}或干脆切换到 Reactor Netty 客户端:
1
2
3
4@Bean
fun webSocketClient(): WebSocketClient {
return ReactorNettyWebSocketClient()
}启动时应看到类似日志:
1
Downstream connection established for session: <id>
实在不行web等依赖都排除tomcat,自行在web依赖加上exclusions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33<!-- 1. Spring Boot Web,但排除默认 Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 排除 Tomcat 嵌入式容器 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 2. 专门引入 Undertow 及其 JSR-356 支持 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- Undertow 对 JSR-356 WebSocket 的实现 -->
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-websockets-jsr</artifactId>
<!-- 可根据 Spring Boot 版本选择合适版本,通常与 Spring Boot 兼容即可 -->
</dependency>
<!-- 3. Spring WebSocket 模块,用于控制握手与消息处理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<!-- 注意:Spring Boot Starter WebSocket 本身会带 Tomcat 的 WebSocket 支持,
但由于我们已经排除了 Tomcat Starter,这里只会引入 spring-websocket 相关依赖,不会再带 tomcat-embed-websocket -->
</dependency>
2. 路由没生效
现象
- 配置了
WebSocketConfigurer
,但afterConnectionEstablished
并未触发 - 日志中不见任何
/your/path
映射信息
原因
- 构造函数注入
List<ISocketHandler>
导致循环依赖 - Spring Boot 2.6+ 默认禁用循环引用,Bean 未注册
- 或者忘记
@EnableWebSocket
排查 & 解决
确保配置类加上:
1
2
3
4
5@Configuration
@EnableWebSocket
class WebSocketConfig(
@Lazy private val handlers: List<ISocketHandler>
) : WebSocketConfigurer { … }日志应包含:
1
Mapping “[/{registerPath}]” to WebSocketHandler
3. API 弃用与签名变化
3.1 Unresolved reference: handshake
/ execute
提示
1 |
|
解读
StandardWebSocketClient
使用doHandshake(...)
而非execute
execute()
属于 WebFlux Reactor 客户端,与 MVC 客户端不通
修正
MVC 客户端:
1
2client.doHandshake(handler, uri)
.addCallback({ sess -> … }, { ex -> … })Reactor 客户端:
1
2
3client.execute(URI.create(uri)) { wsSession ->
…
}.subscribe()
3.2 doHandshake(...)
自 6.0 起弃用
提示
1 |
|
说明
- Spring Framework 6 推荐注入
WebSocketContainer
或切换到 Reactor Netty - 暂可忽略警告,或升级为新版推荐 API
4. 依赖冲突 & 类加载
典型报错
1 |
|
原因
- Spring Boot 3.x 使用
jakarta.websocket
,2.x 使用javax.websocket
- 嵌入式 Tomcat/WebSocket API 版本与项目引入冲突
解决
统一依赖至一个版本,且与 Spring Boot 主版本匹配
必要时在依赖中排除冲突项:
1
2
3
4
5
6
7
8
9
10<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<exclusions>
<exclusion>
<groupId>jakarta.websocket</groupId>
<artifactId>jakarta.websocket-api</artifactId>
</exclusion>
</exclusions>
</dependency>
5. 其他常见错误
错误类型 | 典型现象 / 异常 | 排查要点 |
---|---|---|
循环依赖 | BeanCreationException: circular reference |
使用 @Lazy 或拆分配置 |
无效 JSON | JsonParseException: Unexpected character (‘“’) |
前端必须用英文双引号;捕获异常并 friendly 返回 |
ConcurrentModificationException | java.util.ConcurrentModificationException |
迭代时修改集合;先转成列表再遍历 |
NullPointerException | Cannot invoke "JsonNode.asText()" ... get(...) |
判空或使用 ?.asText(default) |