目录
根本原因:WebSocket 类不是由 Spring 管理的 Bean 实例
虽然加上了 @Component
,但 在 WebSocket 的生命周期回调方法中使用 @Autowired
注入其他 Bean 时会失败,这是因为:
根本原因:WebSocket 类不是由 Spring 管理的 Bean 实例
尽管你加了 @Component
,Spring 确实会把这个类作为一个 Bean 注册到容器中。但是,Java 的 WebSocket API(JSR-356)在创建 @ServerEndpoint
对应的类实例时,并不是通过 Spring 容器来创建的,而是由底层的 WebSocket 容器(如 Tomcat、Jetty)直接 new 出来的。
也就是说:
- Spring 创建了一个
WebSocketServer
实例(作为 Bean) - WebSocket 容器又自己 new 了一个
WebSocketServer
实例(用于处理连接) - 这个 new 出来的实例并没有被 Spring 管理,所以里面的
@Autowired
字段是 null
@Component
@ServerEndpoint("/webSocket/{userId}")
public class WebSocketServer {
@Autowired
private SomeService someService; // 会是 null
@OnOpen
public void onOpen(...) {
someService.doSomething(); // NullPointerException!
}
}
解决方案:手动从 Spring 容器中获取 Bean
你可以通过一个工具类,从 Spring 容器中手动获取你需要的 Bean。
1. 创建 Spring 上下文工具类:
@Component
public class SpringContextUtils implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
public static <T> T getBean(String name, Class<T> beanClass) {
return context.getBean(name, beanClass);
}
}
2. 在 WebSocket 中使用:
@Component
@ServerEndpoint("/webSocket/{userId}")
public class WebSocketServer {
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
SomeService someService = SpringContextUtils.getBean(SomeService.class);
someService.doSomething();
}
}