在ServletContextListener中通过SimpMessagingTemplate向所有客户端发送消息

10

我正在使用Spring框架,我有一个工作的websocket控制器,看起来像这样:

@Controller
public class GreetingController {

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(HelloMessage message) throws InterruptedException {
        return new Greeting("Hello, " + message.getName() + "!");
    }
}

我也有这个配置:

@Configuration
@EnableWebSocketMessageBroker
public class HelloWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/hello").withSockJS();
    }
}

那部分运行得很好!我可以成功地使用 Stomp.js 在两个或多个浏览器之间发送和接收消息。这里是不起作用的部分。我实现了一个ServletContextListener,其中包含一个自定义对象,为简单起见,我称之为“notifier”。通知程序监听服务器端发生的某些事件。然后调用“notify”方法,该方法应向所有客户端发送有关事件的详细信息。但是它不起作用。

@WebListener
public class MessageListener implements ServletContextListener, Notifiable {

    private Notifier notifier;

    @Autowired
    private SimpMessagingTemplate messageSender;


    public MessageListener() {
        notifier = new Notifier(this);
    }

    public void contextInitialized(ServletContextEvent contextEvent) {
        WebApplicationContextUtils
        .getRequiredWebApplicationContext(contextEvent.getServletContext())
        .getAutowireCapableBeanFactory()
        .autowireBean(this);

        notifier.start();
    }

    public void contextDestroyed(ServletContextEvent contextEvent) {
        notifier.stop();
    }

    public void notify(NotifyEvent event) {
        messageSender.convertAndSend("/topic/greetings", new Greeting("Hello, " + event.subject + "!"));
    }
}

我没有收到任何异常。Spring成功注入了SimpMessagingTemplate,所以它不是空的。我已经能够进入Spring代码并发现,在使用SimpMessagingTemplate时,SimpleBrokerMessageHandlersubscriptionRegistry为空。因此,它必须是控制器使用的实例不同。我如何获得与控制器使用的相同的subscriptionRegistry


请问您是否找到了答案。当从Spring ApplicationEvent类调用时,会出现相同的行为。 - Roy Russo
抱歉这么晚才回复。我已经在下面发布了我的解决方案。 - battmanz
2个回答

4
解决方案是使用Spring的ApplicationListener类而不是ServletContextListener,并特别监听ContextRefreshedEvent事件。
以下是我的工作示例:
@Component
public class MessagingApplicationListener implements ApplicationListener<ContextRefreshedEvent>, Notifiable {
    private final NotifierFactor notifierFactory;
    private final MessageSendingOperations<String> messagingTemplate;
    private Notifier notifier;

    @Autowired
    public MessagingApplicationListener(NotifierFactor notifierFactory, MessageSendingOperations<String> messagingTemplate) {
        this.notifierFactory = notifierFactory;
        this.messagingTemplate = messagingTemplate;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (notifier == null) {
            notifier = notifierFactory.create(this);
            notifier.start();
        }
    }

    public void notify(NotifyEvent event) {
        messagingTemplate.convertAndSend("/topic/greetings", new Greeting("Hello, " + event.subject + "!"));
    }

    @PreDestroy
    private void stopNotifier() {
        if (notifier != null) {
            notifier.stop();
        }
    }
}

什么是Notifiable、NotifierFactor和Notifier?请帮忙,我只想调用我的WebSocket。 - Afroz Shaikh
1
这些是我即兴发挥的抽象概念,用来阐述我的观点。然而,它们代表了你的应用程序中可能存在的真实世界服务。如果你正在使用WebSocket,那么我假设你有一些服务器端事件需要推送消息到客户端。这些抽象概念只是代表了那些服务器端事件。 - battmanz
我在同一个类中拥有SimpMessagingTemplate和控制器,我正在调用webservice来使用template调用websocket,没有错误,但我的websocket没有被调用。 - Afroz Shaikh
你能检查一下这个线程并帮我吗?https://dev59.com/k5nga4cB1Zd3GeqPdsy_?noredirect=1#comment65210266_38919790。 - Afroz Shaikh

1
这个解决方案非常好用。对于任何想要快速解决问题的人 - 剥离所有"Notifier"相关内容,只需将MessagingApplicationListener注入到你的类中。然后使用一个字符串调用'notify'方法发送消息。显然, 正如@battmanz所说,你需要一种将事件推送给监听器的方式,但这个类实现了你所需要的基本功能。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接