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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25561741/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 08:13:30  来源:igfitidea点击:

Send Message to all clients via SimpMessagingTemplate in ServletContextListener

javaspringspring-mvcwebsocketspring-websocket

提问by battmanz

I'm using the Spring framework and I have a working websocket controller that looks like this:

我正在使用 Spring 框架,并且我有一个工作的 websocket 控制器,如下所示:

@Controller
public class GreetingController {

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

I also have this configuration:

我也有这个配置:

@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();
    }
}

That part works great! I can successfully send and receive messages between two or more browsers using Stomp.js. Here's the part that doesn't work. I have implemented a ServletContextListenerthat contains a custom object that, for simplicity sake, I have called 'notifier'. The notifier listens for certain events to happen on the server-side. It then calls the 'notify' method, which is supposed to send details about the event to all clients. It doesn't work, though.

那部分效果很好!我可以使用 Stomp.js 在两个或多个浏览器之间成功发送和接收消息。这是不起作用的部分。我已经实现了一个ServletContextListener包含自定义对象的对象,为简单起见,我将其称为“通知程序”。通知程序侦听服务器端发生的某些事件。然后它调用“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 + "!"));
    }
}

I do not get an exception. The SimpMessagingTemplatehas been successfully injected by Spring, so it's not null. I have been able to step into the Spring code and have figured out that the SimpleBrokerMessageHandler's subscriptionRegistryis empty when using the SimpMessagingTemplate. So it must be a separate instance from the one that the controllers are using. How can I get the same subscriptionRegistrythat is used by the controllers?

我没有例外。在SimpMessagingTemplate已经成功地由Spring注入,所以它不是空。我已经能够步入春天代码,并已经想通了,对SimpleBrokerMessageHandlersubscriptionRegistry用时是空的SimpMessagingTemplate。所以它必须是一个独立于控制器使用的实例。我怎样才能得到与subscriptionRegistry控制器使用的相同的东西?

采纳答案by battmanz

The solution was to use Spring's ApplicationListenerclass instead of a ServletContextListener, and to specifically listen for the ContextRefreshedEvent.

解决方案是使用 Spring 的ApplicationListener类而不是 a ServletContextListener,并专门监听ContextRefreshedEvent.

This is my working example:

这是我的工作示例:

@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();
        }
    }
}

回答by obrienk

This solution worked great.

这个解决方案效果很好。

For anybody who wants a quick solution - strip out all the "Notifier" stuff and just inject MessagingApplicationListener into your class. Then call 'notify' with a String to send a message. Obviously, as @battmanz says you'll want some way to push events to the listener, but this class does the basics of what you need.

对于任何想要快速解决方案的人 - 去掉所有“通知程序”的内容,只需将 MessagingApplicationListener 注入您的类。然后使用字符串调用“通知”以发送消息。显然,正如@battmanz 所说,您需要某种方式将事件推送到侦听器,但此类完成了您所需要的基础知识。