websocket 注入 service对象的方法

websocket注入service对象的方法

SpringBoot + WebSocket

本人使用

@Autowired

发现注入不进去,断点发现service对象为null。
猜测是

 @ServerEndpoint(value = "/dataWebSocket", encoders = ServerEncoder.class)

这个注解造成的依赖替代。

使用如下方法 亲测可行

启动类的主方法

    public static void main(String[] args) {
        ConfigurableApplicationContext run = SpringApplication.run(PeehqsApplication.class, args);
        DataStatisticsWebSocket.setApplicationContext(run);
    }

websocket服务类的配置

@ServerEndpoint(value = "/dataWebSocket", encoders = ServerEncoder.class)
@Component
public class DataStatisticsWebSocket{

    private static final Logger LOGGER = LoggerFactory.getLogger(DataStatisticsWebSocket.class);

    /**
     * 存放所有在线的客户端
     */
    private static Map<String, Session> clients = new ConcurrentHashMap<>();

    private static ApplicationContext applicationContext;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        DataStatisticsWebSocket.applicationContext = applicationContext;
    }

    @OnOpen
    public void onOpen(Session session) {
        LOGGER.info("有新的客户端连接了dataWebSocket: {}", session.getId());
        //将新用户存入在线的组
        clients.put(session.getId(), session);
    }

    /**
     * 客户端关闭
     * @param session session
     */
    @OnClose
    public void onClose(Session session) {
        LOGGER.info("有用户断开dataWebSocket了, id为:{}", session.getId());
        //将掉线的用户移除在线的组里
        clients.remove(session.getId());
    }

    /**
     * 发生错误
     * @param throwable e
     */
    @OnError
    public void onError(Throwable throwable) {
        LOGGER.error("dataWebSocket发生错误");
    }

    /**
     * 收到客户端发来消息
     * @param message  消息对象
     */
    @OnMessage
    public void onMessage(String message) {
        LOGGER.info("dataWebSocket服务端收到客户端发来的消息: {}", message);
        DataStatisticsService dataStatisticsService = applicationContext.getBean(DataStatisticsService.class);
        dataStatisticsService.statisticsRefresh();
    }


    /**
     * 群发消息
     * @param message 消息内容
     */
    public void sendAll(String message) {
        for (Map.Entry<String, Session> sessionEntry : clients.entrySet()) {
            sessionEntry.getValue().getAsyncRemote().sendText(message);
        }
    }

    /**
     * @param message 消息内容
     */
    public void sendObject(Object message) {
        for (Map.Entry<String, Session> sessionEntry : clients.entrySet()) {
            LOGGER.info("dataWebSocket发送内容:{}",message.toString());
            sessionEntry.getValue().getAsyncRemote().sendObject(message);
        }
    }

    /**
     * 发送消息
     * @param session
     * @param message
     * @throws IOException
     */
    public void sendMessage(Session session, String message) throws IOException {
        if(session != null){
            try {
                session.getAsyncRemote().sendText(message);
            }
            catch (Exception e) {
                LOGGER.error("websocket发送信息失败!{}", e.getMessage());
            }
        }
    }


}

附上 websocket 发送对象的编码类

public class ServerEncoder implements Encoder.Text<OperateResult> {
    @Override
    public String encode(OperateResult operateResult)  {
        Object obj = JSONObject.toJSON(operateResult);
        return obj.toString();
    }

    @Override
    public void init(EndpointConfig endpointConfig) {

    }

    @Override
    public void destroy() {

    }
}
### SpringBoot 中 WebSocket Mapper 注入失败的原因分析 在 SpringBoot 项目中,WebSocket 的 Bean 是多例的(即每次客户端连接都会创建一个新的 WebSocket 对象),而 Spring 容器中的其他 Bean 默认是单例的。由于 WebSocket 的生命周期是由客户端连接触发的,而不是由 Spring 容器管理的,因此在 WebSocket 初始化时无法直接注入依赖于 Spring 容器管理的 Bean,比如 MyBatis 的 Mapper。 #### 解决方案一:使用 ApplicationContext 获取 Bean 可以利用 `ApplicationContext` 动态获取所需的 Bean。通过这种方式,在 WebSocket 运行期间手动从 Spring 容器中获取 Mapper 或者 Service 层的对象。 以下是具体实现方法: ```java @Component public class MyWebSocketHandler extends TextWebSocketHandler { private final ApplicationContext applicationContext; public MyWebSocketHandler(ApplicationContext applicationContext) { this.applicationContext = applicationContext; } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 使用 ApplicationContext 手动获取 Mapper UserMapper userMapper = applicationContext.getBean(UserMapper.class); // 调用 Mapper 方法 List<User> users = userMapper.getAllUsers(); System.out.println(users); super.handleTextMessage(session, message); } } ``` 此方式适用于需要动态加载 Bean 场景[^1]。 --- #### 解决方案二:将 WebSocket 配置为单例模式并延迟初始化 如果希望保持 WebSocket 单例模式,则可以在其内部定义一个懒加载机制来处理依赖注入问题。例如,通过代理类或者工厂方法完成对 Mapper 的访问。 下面是一个基于 Factory Pattern 的例子: ```java @Component public class WebSocketFactory { @Autowired private UserMapper userMapper; public UserMapper getUserMapper() { return userMapper; } } @Component public class MyWebSocketHandler extends TextWebSocketHandler { @Autowired private WebSocketFactory webSocketFactory; @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 通过工厂类间接调用 Mapper UserMapper userMapper = webSocketFactory.getUserMapper(); // 查询数据库操作 List<User> users = userMapper.getAllUsers(); System.out.println(users); super.handleTextMessage(session, message); } } ``` 这种方法能够有效分离 WebSocket 和 Mapper 的耦合关系[^2]。 --- #### 解决方案三:调整 WebSocket 生命周期至 Spring 管理范围 另一种思路是让 WebSocket 成为 Spring 容器完全托管的一个组件。这样就可以像普通 Controller 类一样正常注入 Mapper 或服务层逻辑。 修改后的代码如下所示: ```java @Component public class MyWebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myWebSocketHandler(), "/websocket").setAllowedOrigins("*"); } @Bean public WebSocketHandler myWebSocketHandler() { return new MyWebSocketHandler(); } } @Component public class MyWebSocketHandler extends TextWebSocketHandler { @Autowired private UserMapper userMapper; @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 正常调用已注入的 Mapper List<User> users = userMapper.getAllUsers(); System.out.println(users); super.handleTextMessage(session, message); } } ``` 上述配置使得 WebSocket 可以被 Spring 容器统一管理,并支持标准的 DI 模型[^3]。 --- ### 总结 以上三种方法分别针对不同场景提供了灵活的选择: - 如果仅需临时获取某个特定 Bean,推荐 **解决方案一**; - 若涉及复杂业务流程且不希望增加额外依赖,可采用 **解决方案二**; - 若要彻底融入 Spring 生态体系,则应优先考虑 **解决方案三**。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值