webSocket中使用Autowired注入为空
项目里刚加了个 WebSocket 服务,中d注结果一上线就发现 这事儿不复杂,使用但坑挺典型。中d注Spring 管理 Bean 的使用默认规则是:谁创建,谁负责注入。中d注你写一个 关键点在这:每个 WebSocket 连接都会创建一个新的 Endpoint 实例。不是单例,不是原型 Bean 那种“每次请求新建”,而是 Tomcat 自己管理的生命周期。你的 解决办法有几个方向。最直接的是把 WebSocket 端点也交给 Spring 管。Spring 提供了 但注意,这个方案只在嵌入式容器(比如 Spring Boot 内嵌 Tomcat)下有效。如果你把 war 包丢到外部 Tomcat 里跑, 另一种常见方案是静态注入。写一个 然后在 Endpoint 里: 这里有个细节: 还有第三种,用 我自己踩过的坑是:用了 另外,WebSocket 端点里注入的 Bean 如果是 说到底,@Autowired 注入的使用 Bean 全是 null。不是中d注偶尔,是使用稳定复现——每次客户端连上来,ServerEndpoint 实例里的中d注 service 字段就是空的。断点调试进去看,使用构造函数执行了,中d注字段声明也在,使用就是中d注没人给它赋值。@Component 或者 @Service,使用Spring 容器启动时扫描到它,中d注创建实例,然后处理 @Autowired 字段。整个过程由 Spring 掌控。但 WebSocket 的 @ServerEndpoint 不是 Spring 创建的——它是 JSR-356 规范下的东西,由 Servlet 容器(Tomcat、Jetty 等)负责实例化。Tomcat 发现一个类标注了 @ServerEndpoint,就自己 new 一个出来,跟 Spring 容器半毛钱关系没有。Spring 根本没机会插手,自然也不会去注入什么字段。@Autowired 写在字段上,Spring 看不到这个实例,注入无从谈起。ServerEndpointExporter,在配置类里注册这个 Bean,它会把 @ServerEndpoint 标注的类注册到 Spring 容器中,后续连接创建实例时由 Spring 负责依赖注入。代码大概长这样:@Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } } ServerEndpointExporter 会报错,因为它依赖 Servlet 容器对 ServerContainer 的支持方式不同。外部容器场景下,得用另一套做法。@Component 类实现 ApplicationContextAware,把 ApplicationContext 存到静态字段里。然后在 WebSocket 端点中通过 ApplicationContext.getBean() 手动获取依赖。代码不优雅,但管用。比如:@Component public class SpringContextHolder implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext ctx) { context = ctx; } public static <T> T getBean(Class<T> clazz) { return context.getBean(clazz); } } @ServerEndpoint("/ws") @Component public class MyWebSocket { private UserService userService = SpringContextHolder.getBean(UserService.class); } @Component 加在 Endpoint 上是为了让 Spring 扫描到它,但实际实例化还是 Tomcat 做的。静态 holder 保证在 Tomcat 创建实例时,Spring 容器已经启动完毕,context 不为空。顺序不能反,否则拿到的还是 null。@Configurator 自定义 ServerEndpointConfig.Configurator,重写 getEndpointInstance 方法,从 Spring 容器里拿 Bean 返回。这个方式更干净,但配置稍繁琐,适合对代码整洁度有要求的场景。ServerEndpointExporter,本地跑得好好的,一上测试环境就挂。查了半天发现测试环境是外部 Tomcat,ServerEndpointExporter 直接抛异常导致整个应用起不来。后来换成静态 holder 方案才消停。所以选方案之前,先确认部署环境是嵌入式还是外部容器,这个前提比代码本身重要。@Scope("prototype") 或者有状态的对象,得留意线程安全问题。每个连接一个 Endpoint 实例,但如果你在 Endpoint 里开了多线程处理消息,共享的 service 仍然可能出并发问题。这不是注入的问题,但很容易在解决注入之后被忽略。@Autowired 为空不是 Spring 的 bug,是生命周期归属没对齐。谁创建实例,谁负责注入。Tomcat 创建的实例,Spring 管不着。把创建权拿回来,或者手动去容器里取,问题就解决了。