Springboot使用Websocket的时候调取IOC管理的Bean报空指针异常问题
作者:堕落年代
这篇文章主要介绍了Springboot使用Websocket的时候调取IOC管理的Bean报空指针异常问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
问题
这个问题主要是因为Websocket的工作方式导致的,下面是详细解决方案
解决
WebSocket 端点类通常不受 Spring IOC 管理的原因在于它们是由 WebSocket 容器(例如,Tomcat、Jetty 等)而不是 Spring 容器管理的。
WebSocket 规范(JSR 356)定义了 WebSocket 端点的生命周期和管理方式,这通常与 Spring 的生命周期和依赖注入机制不同。
以下是一些具体原因和解决方法:
原因
不同的生命周期管理:
- WebSocket 端点的创建和管理是由 Web 容器(如 Tomcat、Jetty 等)负责的,而不是由 Spring 容器负责。
- 这意味着 WebSocket 端点类的实例化和生命周期管理不在 Spring 的控制范围内。
注入机制不同:
- Spring 的依赖注入机制依赖于 Spring 容器管理的 Bean,但 WebSocket 端点类实例化时,Web 容器并不知道如何进行依赖注入。
解决方法
方法一:使用 Spring Boot 和 @Configuration 配置
在 Spring Boot 项目中,可以通过配置类和自定义 WebSocket 处理器来管理 WebSocket 连接,这样可以使用 Spring 容器管理的 Bean。
创建 WebSocket 配置类:
import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { private final MyWebSocketHandler myWebSocketHandler; public WebSocketConfig(MyWebSocketHandler myWebSocketHandler) { this.myWebSocketHandler = myWebSocketHandler; } @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myWebSocketHandler, "/websocket").setAllowedOrigins("*"); } }
创建 WebSocket 处理器类:
import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import org.springframework.stereotype.Component; @Component public class MyWebSocketHandler extends TextWebSocketHandler { private final SomeService someService; public MyWebSocketHandler(SomeService someService) { this.someService = someService; } @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { String payload = message.getPayload(); System.out.println("消息为:" + payload); someService.doSomething(); // 使用 Spring Bean session.sendMessage(new TextMessage("servet 发送:" + payload)); } }
方法二:使用自定义 SpringConfigurator
创建自定义的 SpringConfigurator
:
import javax.websocket.server.ServerEndpointConfig; import org.springframework.web.context.ContextLoader; import org.springframework.web.context.WebApplicationContext; public class SpringConfigurator extends ServerEndpointConfig.Configurator { @Override public <T> T getEndpointInstance(Class<T> clazz) throws InstantiationException { WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext(); if (context == null) { throw new InstantiationException("Unable to get Spring context."); } return context.getAutowireCapableBeanFactory().createBean(clazz); } }
在 @ServerEndpoint
注解中指定 configurator
:
import javax.websocket.OnMessage; import javax.websocket.server.ServerEndpoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @ServerEndpoint(value = "/websocket", configurator = SpringConfigurator.class) @Component public class MyWebSocket { @Autowired private SomeService someService; // 由 Spring 管理的 Bean @OnMessage public String onMsg(String text) throws IOException { System.out.println("消息为:" + text); someService.doSomething(); // 使用 Spring Bean return "servet 发送:" + text; } }
方法三:手动获取 Spring Bean
创建 ApplicationContextProvider 类:
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class ApplicationContextProvider implements ApplicationContextAware { private static ApplicationContext context; @Override public void setApplicationContext(ApplicationContext applicationContext) { context = applicationContext; } public static ApplicationContext getApplicationContext() { return context; } }
在 WebSocket 类中使用 ApplicationContextProvider 获取 Bean:
import javax.websocket.OnMessage; import javax.websocket.server.ServerEndpoint; import org.springframework.stereotype.Component; @ServerEndpoint(value = "/websocket") @Component public class MyWebSocket { private SomeService someService; public MyWebSocket() { this.someService = ApplicationContextProvider.getApplicationContext().getBean(SomeService.class); } @OnMessage public String onMsg(String text) throws IOException { System.out.println("消息为:" + text); someService.doSomething(); // 使用 Spring Bean return "servet 发送:" + text; } }
通过这些方法,你可以确保 WebSocket 端点类能够正确地访问由 Spring IOC 管理的 Bean,从而避免空指针异常。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。