You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring WebSocket实现服务端主动推送倒计时数值至所有客户端

实现Spring Boot服务端主动推送倒计时至WebSocket客户端

问题根源

你遇到的空指针异常是因为Countdown不是Spring容器管理的Bean,无法自动注入SimpMessagingTemplate;同时在main方法中直接new线程的方式脱离了Spring上下文,导致依赖无法注入。

解决方案步骤

1. 将Countdown转为Spring管理的Bean并注入SimpMessagingTemplate

修改Countdown类,添加@Component注解,通过构造器注入SimpMessagingTemplate,并在倒计时逻辑中加入推送代码:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;

@Component
public class Countdown implements Runnable {
    private volatile int count = 0;
    private final SimpMessagingTemplate messagingTemplate;

    // 构造器注入SimpMessagingTemplate
    @Autowired
    public Countdown(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    @Override
    public void run() {
        this.runTimer();
    }

    public void runTimer() {
        count = 60;
        while (count > 0) {
            System.out.println("Countdown : " + count + " " + Thread.currentThread().getId());
            try {
                count--;
                // 推送当前count值到所有订阅/topic/greetings的客户端
                messagingTemplate.convertAndSend("/topic/greetings", new CountdownModel(count));
                Thread.sleep(1000);
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }
}

2. 调整线程启动方式,在Spring容器初始化后启动

不要在main方法中直接new线程,而是通过Spring的初始化回调确保线程在容器就绪后启动,比如实现ApplicationRunner接口:

import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    // 容器初始化完成后启动倒计时线程
    @Bean
    public ApplicationRunner countdownRunner(Countdown countdown) {
        return args -> {
            Thread th = new Thread(countdown);
            th.start();
        };
    }
}

3. 确保CountdownModel可序列化

CountdownModel需要是标准POJO,提供getter方法以便客户端解析字段:

public class CountdownModel {
    private int count;

    public CountdownModel(int count) {
        this.count = count;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }
}

4. 简化WebSocket控制器(可选)

如果不需要客户端触发推送的逻辑,控制器可简化为仅保留必要结构;若需客户端交互则保留原有方法:

import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class CountdownWSController {
    private final SimpMessagingTemplate template;

    public CountdownWSController(SimpMessagingTemplate template) {
        this.template = template;
    }

    // 保留客户端触发的方法(若需要)
    // @MessageMapping("/client")
    // public void countDownService(int count) throws Exception{
    //     this.template.convertAndSend("/topic/greetings", new CountdownModel(count));
    // }
}

关键说明

  • 让Countdown成为Spring Bean后,Spring会自动注入SimpMessagingTemplate,彻底解决空指针问题。
  • 使用ApplicationRunner确保线程在Spring容器完全初始化后启动,此时所有依赖都已就绪。
  • volatile关键字保证count值的可见性,确保线程能读取到最新值;如果是多线程操作count,建议使用AtomicInteger保证原子性。

内容的提问来源于stack exchange,提问作者DKB

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 12:53:43