@ServerEndpoint不生效的根本原因是WebSocket容器实现缺失或Tomcat未启用WebSocket支持,需确保tomcat-embed-websocket依赖正确、避免spring-boot-starter-websocket干扰、统一使用javax.websocket-api 1.1,并通过ServletContext手动注册端点。

为什么 @ServerEndpoint 注解不生效?
根本原因通常是类路径下缺少 WebSocket 容器实现,或者容器(比如 Tomcat)没启用 WebSocket 支持。Spring Boot 2.3+ 默认用的是 Tomcat 9+,但如果你用的是嵌入式 Tomcat,@ServerEndpoint 不会自动扫描——它不走 Spring 的组件扫描机制,而是依赖 Servlet 容器的原生 WebSocket 扩展。
- Tomcat 需要确保
tomcat-websocket.jar在 classpath(Maven 里对应org.apache.tomcat.embed:tomcat-embed-websocket) - Spring Boot 项目中,若同时引入了
spring-boot-starter-websocket,反而可能干扰原生@ServerEndpoint,因为该 starter 会注册自己的 WebSocket 配置,覆盖掉容器级支持 - IDE 运行时(如 IntelliJ)若用 “Spring Boot Run Configuration”,默认不会激活 Tomcat 的 WebSocket 模块,得手动确认依赖和启动方式
如何让 @ServerEndpoint 被 Tomcat 正确加载?
关键不是加注解,而是让 Tomcat 主动发现并注册这个端点。最稳妥的方式是通过 ServerEndpointConfig.Builder + ServletContext 手动注册,绕过自动扫描的不可靠性。
- 在
@ServletComponentScan或ServletContainerInitializer中注册,但更简单的是在@Configuration类里写一个@Bean方法,返回ServerEndpointExporter(仅适用于 Spring WebFlux 或老版本 Spring;Spring Boot 2.5+ 已弃用) - 推荐做法:不依赖自动扫描,在
ServletContextInitializer中调用server.getServletContext().addServerEndpoint(...) - 示例片段:
registry.addServlet(new ServletRegistrationBean<>(new ServerEndpointRegistration(), "/echo"));
实际上不成立——ServerEndpointRegistration不是 Servlet,必须用ServerEndpointConfig.Builder.create(MyEndpoint.class, "/echo").build()构造后交给容器
Maven 依赖冲突导致 @ServerEndpoint 编译通过但运行报错
典型错误是 java.lang.NoClassDefFoundError: javax/websocket/Endpoint 或启动时报 ClassNotFoundException: org.apache.tomcat.websocket.server.WsSci,说明 JSR-356 API 和实现没对齐。
- 确保只有一套 WebSocket API:用
javax.websocket-api(1.1),不要混用jakarta.websocket-api(2.0+),后者是 Jakarta EE 9+,与 Tomcat 9/10 兼容性有坑 - Tomcat 9 对应
javax.websocket-api:1.1,Tomcat 10.1+ 才正式支持jakarta.websocket-api:2.1;若强行在 Tomcat 9 里引 jakarta 包,@ServerEndpoint注解会被忽略且无提示 - 检查
mvn dependency:tree -Dincludes=websocket,删掉重复或版本错配的依赖,尤其是spring-boot-starter-websocket带来的tomcat-embed-websocket和你手动引的冲突
开发调试时容易忽略的容器初始化时机
@ServerEndpoint 类的静态块、构造方法、@OnOpen 方法执行顺序,取决于容器何时完成扫描和实例化——它不等 Spring 上下文就绪,所以不能在 @OnOpen 里直接注入 @Autowired Bean。
立即学习“Java免费学习笔记(深入)”;
- 如果需要访问 Spring Bean,必须通过
ApplicationContextProvider手动获取,或改用 Spring 的WebSocketHandler+WebSocketConfigurer方案 - 日志打不出来?因为
@OnOpen可能在 Logback 初始化前执行,建议先用System.out.println确认是否进入 - 路径匹配失败常见于 URL 写成
/ws/echo却在客户端连ws://localhost:8080/echo,注意上下文路径(context-path)是否影响前缀










