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

标签 java spring spring-mvc websocket spring-websocket

我正在使用 Spring 框架,我有一个工作的 websocket Controller ,如下所示:

@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,其中包含一个自定义对象,为简单起见,我将其称为“通知程序”。通知程序监听服务器端发生的某些事件。然后它调用“通知”方法,该方法应该将有关事件的详细信息发送给所有客户端。但是,它不起作用。

@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 + "!"));
    }
}

我没有得到异常。 SimpMessagingTemplate 已被 Spring 成功注入(inject),因此它不为空。我已经能够进入 Spring 代码并发现在使用 SimpMessagingTemplateSimpleBrokerMessageHandlersubscriptionRegistry 是空的。因此它必须是与 Controller 正在使用的实例不同的实例。我怎样才能获得 Controller 使用的相同 subscriptionRegistry

最佳答案

解决方案是使用 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();
        }
    }
}

关于java - 通过 ServletContextListener 中的 SimpMessagingTemplate 向所有客户端发送消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25561741/

相关文章:

java - Spring MVC RESTful 多 View - 404 未找到

java - JPA 的单元测试用例值得吗?无论如何,它只是要访问数据库,为什么我们需要它?

java - 关于java中无限期运行的小部件

java - 使用 ajax 将列表发送到我的 Spring Controller

Spring Boot 应用程序作为 systemd 服务 : log file

java - 有没有办法在 JPA 查询中从数据库中选择特定列

java - Jackson:仅从接口(interface)导出 setter/getter

java - bean实例化失败;嵌套异常是 java.lang.NoClassDefFoundError : org/springframework/jdbc/core/RowMapper

java - 无法使用 Spring 在 AppFog 上维持 session

java - 是否可以在运行时向正在运行的 Java Web 应用程序添加新的 JPA/Spring 实体