1. JSR-356 容器握手失败
现象
- 控制台没有任何
afterConnectionEstablished日志 - 客户端卡在握手阶段或报错超时
原因
- Spring MVC 默认用
StandardWebSocketClient(),但未指定底层WebSocketContainer - 嵌入式 Tomcat(JSR-356 实现)与默认容器不匹配,导致握手不发起
排查 & 解决
-
显式注入 JSR-356 容器:
@Bean fun webSocketClient(): WebSocketClient { val container = ContainerProvider.getWebSocketContainer() return StandardWebSocketClient(container) } -
或干脆切换到 Reactor Netty 客户端:
@Bean fun webSocketClient(): WebSocketClient { return ReactorNettyWebSocketClient() } -
启动时应看到类似日志:
Downstream connection established for session: <id> -
实在不行web等依赖都排除tomcat,自行在web依赖加上exclusions:
<!-- 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
排查 & 解决
-
确保配置类加上:
@Configuration @EnableWebSocket class WebSocketConfig( @Lazy private val handlers: List<ISocketHandler> ) : WebSocketConfigurer { … } -
日志应包含:
Mapping “[/{registerPath}]” to WebSocketHandler
3. API 弃用与签名变化
3.1 Unresolved reference: handshake / execute
提示
Unresolved reference 'handshake'
Unresolved reference 'execute'
解读
StandardWebSocketClient使用doHandshake(...)而非executeexecute()属于 WebFlux Reactor 客户端,与 MVC 客户端不通
修正
-
MVC 客户端:
client.doHandshake(handler, uri) .addCallback({ sess -> … }, { ex -> … }) -
Reactor 客户端:
client.execute(URI.create(uri)) { wsSession -> … }.subscribe()
3.2 doHandshake(...) 自 6.0 起弃用
提示
'doHandshake(WebSocketHandler, String, Object...)' 自版本 6.0 起已弃用并标记为移除
说明
- Spring Framework 6 推荐注入
WebSocketContainer或切换到 Reactor Netty - 暂可忽略警告,或升级为新版推荐 API
4. 依赖冲突 & 类加载
典型报错
ClassNotFoundException: javax.websocket.ContainerProvider
NoSuchMethodError: jakarta.websocket.ContainerProvider.getWebSocketContainer()
原因
- Spring Boot 3.x 使用
jakarta.websocket,2.x 使用javax.websocket - 嵌入式 Tomcat/WebSocket API 版本与项目引入冲突
解决
-
统一依赖至一个版本,且与 Spring Boot 主版本匹配
-
必要时在依赖中排除冲突项:
<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) |
浙公网安备 33010602011771号