1. JSR-356 容器握手失败

现象

  • 控制台没有任何 afterConnectionEstablished 日志
  • 客户端卡在握手阶段或报错超时

原因

  • Spring MVC 默认用 StandardWebSocketClient(),但未指定底层 WebSocketContainer
  • 嵌入式 Tomcat(JSR-356 实现)与默认容器不匹配,导致握手不发起

排查 & 解决

  1. 显式注入 JSR-356 容器:

    1
    2
    3
    4
    5
    @Bean
    fun webSocketClient(): WebSocketClient {
    val container = ContainerProvider.getWebSocketContainer()
    return StandardWebSocketClient(container)
    }
  2. 或干脆切换到 Reactor Netty 客户端:

    1
    2
    3
    4
    @Bean
    fun webSocketClient(): WebSocketClient {
    return ReactorNettyWebSocketClient()
    }
  3. 启动时应看到类似日志:

    1
    Downstream connection established for session: <id>
  4. 实在不行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. 确保配置类加上:

    1
    2
    3
    4
    5
    @Configuration
    @EnableWebSocket
    class WebSocketConfig(
    @Lazy private val handlers: List<ISocketHandler>
    ) : WebSocketConfigurer { … }
  2. 日志应包含:

    1
    Mapping “[/{registerPath}]” to WebSocketHandler

3. API 弃用与签名变化

3.1 Unresolved reference: handshake / execute

提示

1
2
Unresolved reference 'handshake'
Unresolved reference 'execute'

解读

  • StandardWebSocketClient 使用 doHandshake(...) 而非 execute
  • execute() 属于 WebFlux Reactor 客户端,与 MVC 客户端不通

修正

  • MVC 客户端:

    1
    2
    client.doHandshake(handler, uri)
    .addCallback({ sess -> … }, { ex -> … })
  • Reactor 客户端:

    1
    2
    3
    client.execute(URI.create(uri)) { wsSession ->

    }.subscribe()

3.2 doHandshake(...) 自 6.0 起弃用

提示

1
'doHandshake(WebSocketHandler, String, Object...)' 自版本 6.0 起已弃用并标记为移除

说明

  • Spring Framework 6 推荐注入 WebSocketContainer 或切换到 Reactor Netty
  • 暂可忽略警告,或升级为新版推荐 API

4. 依赖冲突 & 类加载

典型报错

1
2
ClassNotFoundException: javax.websocket.ContainerProvider
NoSuchMethodError: jakarta.websocket.ContainerProvider.getWebSocketContainer()

原因

  • 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)

本站由 钟意 使用 Stellar 1.28.1 主题创建。
又拍云 提供CDN加速/云存储服务
vercel 提供托管服务
湘ICP备2023019799号-1
总访问 次 | 本页访问