Springboot使用WebSocket无法注入service的解决方法

因为websocket需要开一个自己的线程,如果采用注解的话就是把它当做一个普通的组件放在容器里,调用的时候会一直在spring的线程里,而不是自己独立的线程。所以无法注入service.

解决方法:

spring-boot启动类

@SpringBootApplication
@MapperScan("com.hiramgames.dao")
public class HiramgamesApplication {
	public static void main(String[] args) {
		ConfigurableApplicationContext applicationContext = SpringApplication.run(HiramgamesApplication.class, args);
		myWebSocket.setApplicationContext(applicationContext);
	}
}

 webSocket处理类

在WevSocket处理类中注入SessionManager类

@Component
@ServerEndpoint(value = "/test/{userId}")//socket访问地址
public class WebSocketHandler {   

    //无法使用@Autowired直接注入,运行时会报空指针异常,因为SessionManager没有被注入进来
    //@Autowired
    //private static SessionManager sessionManager;
    
    //SessionManager
    private static SessionManager sessionManager;
    /**
     * 无法通过@Resource注解导入的解决方案
     */
    public static void setApplicationContext(ApplicationContext context) {
        sessionManager = context.getBean(SessionManager.class);
    }
}

 

 

### 解决 Spring WebSocket 中调用 Service `getById` 方法出现 NullPointerException 的问题 在处理 Spring Boot 应用中的 WebSocket 连接时,如果尝试通过 `@Autowired` 注入服务组件并调用其方法(如 `getById`),可能会遇到 `NullPointerException`。这主要是因为 WebSocket 处理器不是由 Spring 容器管理的单例 bean,而是每次建立新连接时都会创建新的实例。 #### 问题分析 WebSocket 和 Spring 单例模式存在差异。每当有新的客户端发起 WebSocket 请求时,服务器端就会创建一个新的 WebSocket 对象来维护该特定会话[^1]。然而,在默认情况下,Spring 只会在启动阶段初始化一次被标记为 `@Component`, `@Service` 或者其他 stereotype 注解的对象作为单例bean存放在IoC容器里。因此,直接利用构造函数或字段上的 `@Autowired` 来获取这些依赖项对于非托管对象来说通常是不可行的,除非采取额外措施确保它们能够访问到正确的上下文环境。 #### 实现方案一:静态化 ApplicationContext 获取 Bean 一种常见的做法是在应用程序配置类中设置一个静态变量用于存储当前的应用程序上下文(`ApplicationContext`),并通过这个静态成员去查找所需的Bean: ```java public abstract class ContextAware { private static ApplicationContext context; public static void setApplicationContext(ApplicationContext applicationContext){ this.context = applicationContext; } @SuppressWarnings("unchecked") protected <T> T getBean(Class<T> clazz) { return (T)context.getBean(clazz); } } ``` 接着可以在自定义的 WebSocket Handler 类继承上述基类,并重写其中的方法以便于获得想要的服务接口实例: ```java @Component public class MyWebSocketHandler extends TextWebSocketHandler implements ContextAware { private final UserService userService; @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { super.afterConnectionEstablished(session); // 使用静态方法从应用上下文中取得UserService实例 userService = getBean(UserService.class); String userId = extractUserIdFromSession(session); // 自定义逻辑提取用户ID User user = userService.getById(userId); ... } // 其他必要的生命周期回调... } ``` 为了使这段代码生效,还需要修改主应用程序入口处以注册全局可用的应用上下文引用: ```java @SpringBootApplication public class WebsocketDemoApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(WebsocketDemoApplication.class, args); ContextAware.setApplicationContext(run); } } ``` 这种方法可以有效地绕过因多线程或多实例造成的自动装配失败情况,从而允许我们在任何地方安全地检索所需的服务资源而不会引发空指针异常。 #### 实现方案二:使用工厂模式构建处理器 另一种更优雅的方式是采用工厂设计模式来生产带有适当依赖关系注入WebSocket 处理器实例。具体而言就是编写一个专门负责制造此类对象的工厂类,它接受来自外部传入的参数以及内部已知的各种辅助工具和服务接口,最终返回完全组装好的目标实体供后续操作之需。 这种方式不仅有助于提高系统的可测试性和灵活性,同时也简化了跨模块间的协作流程,减少了硬编码式的耦合度。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值