Springboot使用Websocket的时候调取IOC管理的Bean报空指针异常

问题

这个问题主要是因为Websocket的工作方式导致的,下面是详细解决方案

解决

WebSocket 端点类通常不受 Spring IOC 管理的原因在于它们是由 WebSocket 容器(例如,Tomcat、Jetty 等)而不是 Spring 容器管理的。WebSocket 规范(JSR 356)定义了 WebSocket 端点的生命周期和管理方式,这通常与 Spring 的生命周期和依赖注入机制不同。以下是一些具体原因和解决方法:

原因

  1. 不同的生命周期管理
    WebSocket 端点的创建和管理是由 Web 容器(如 Tomcat、Jetty 等)负责的,而不是由 Spring 容器负责。这意味着 WebSocket 端点类的实例化和生命周期管理不在 Spring 的控制范围内。

  2. 注入机制不同
    Spring 的依赖注入机制依赖于 Spring 容器管理的 Bean,但 WebSocket 端点类实例化时,Web 容器并不知道如何进行依赖注入。

解决方法

方法一:使用 Spring Boot 和 @Configuration 配置

在 Spring Boot 项目中,可以通过配置类和自定义 WebSocket 处理器来管理 WebSocket 连接,这样可以使用 Spring 容器管理的 Bean。

  1. 创建 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("*");
        }
    }
    
  2. 创建 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
  1. 创建自定义的 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);
        }
    }
    
  2. @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
  1. 创建 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;
        }
    }
    
  2. 在 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,从而避免空指针异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

堕落年代

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值