spring boot 中应弃用 @serverendpoint,改用 websockethandler + websocketconfigurer 模式;若坚持使用 @serverendpoint,必须配置 serverendpointexporter bean 并确保 javax.websocket-api 依赖 scope 为 compile。

Spring Boot + @ServerEndpoint 二选一即可,但别混用——混用会导致端点注册失败、onOpen 不触发、甚至应用启动报 IllegalStateException: No suitable default RequestUpgradeStrategy。
Spring Boot 中误用 @ServerEndpoint 导致 WebSocket 不工作
Spring Boot 默认使用内嵌容器(如 Tomcat),而原生 @ServerEndpoint 依赖 Servlet 容器的 WebSocketContainer 自动扫描和注册。但 Spring Boot 的自动配置会覆盖或干扰该机制,结果就是:类被加载了,@OnOpen 方法从不执行,浏览器连接返回 404 或直接断连。
- 现象:访问
ws://localhost:8080/chat时控制台无日志,浏览器 Network 面板显示“Error during WebSocket handshake” - 原因:Spring Boot 的
TomcatServletWebServerFactory未启用 WebSocket 支持,且未将ServerEndpointExporter注入上下文 - 修复方式(仅当坚持用原生 API):
@Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); }并确保javax.websocket-api依赖 scope 为compile(不能是provided) - 更稳妥做法:直接弃用
@ServerEndpoint,改用 Spring 的WebSocketHandler+WebSocketConfigurer模式,与 Spring 生命周期对齐
WebSocketHandler 中如何安全广播消息给所有客户端
用 ConcurrentHashMap<session string></session> 手动管理会话看似简单,但极易在 afterConnectionClosed 里漏删 Session,造成内存泄漏;而 Spring 的 SimpMessagingTemplate 在 STOMP 场景下又不适用于纯 WebSocket 端点。
- 推荐方案:继承
TextWebSocketHandler,用CopyOnWriteArraySet存储WebSocketSession实例(不是Session) - 关键点:
afterConnectionEstablished中添加 session,afterConnectionClosed中移除——且必须用session.getId()做判等,因为 session 对象可能被重建 - 广播时避免
session.isOpen()判定失效:某些网络抖动下isOpen()返回true,但sendMessage()抛IOException,应包 try-catch 并主动清理异常 session - 示例片段:
private final Set<websocketsession> sessions = new CopyOnWriteArraySet(); @Override public void afterConnectionEstablished(WebSocketSession session) { sessions.add(session); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { sessions.removeIf(s -> s.getId().equals(session.getId())); }</websocketsession>
为什么 setAllowedOriginPatterns("*") 在生产环境必须改掉
setAllowedOriginPatterns("*") 看似方便,但实际会触发浏览器拒绝连接——现代浏览器(Chrome 91+、Firefox 89+)对通配符 origin 的 WebSocket 连接要求必须同时设置 credentials = false,而 Spring 默认允许携带 cookie,导致预检失败或握手被中断。
- 错误现象:控制台报
WebSocket connection to 'wss://xxx' failed: Error during WebSocket handshake,Network 面板中 upgrade 请求状态为 (failed) - 真正安全的写法是显式列出可信域名:
setAllowedOriginPatterns("https://app.example.com", "https://staging.example.com") - 若需支持子域(如 user1.app.example.com),用
"https://*.example.com",注意开头必须带协议,且不能混用http和https - 本地开发可临时保留
"http://localhost:*",但 CI/CD 流水线中应通过 profile 切换配置
真正难处理的不是连接建立,而是连接断开后的状态同步——比如用户 A 断线 3 秒后重连,B 已发 5 条消息,这些消息要不要补推?这需要引入消息队列或会话级序列号,远超 WebSocketHandler 能力范围。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











