返回介绍

18.3.3 发送消息到客户端

发布于 2024-08-17 00:45:49 字数 3749 浏览 0 评论 0 收藏 0

到目前为止,客户端负责了所有的消息发送,服务器只能监听这些消息。对于WebSocket和STOMP来说,这是一种合法的用法,但是当你考虑使用WebSocket的时候,所设想的使用场景恐怕并非如此。WebSocket通常视为服务器发送数据给浏览器的一种方式,采用这种方式所发送的数据不必位于HTTP请求的响应中。使用Spring和WebSocket/STOMP的话,该如何与基于浏览器的客户端通信呢?

Spring提供了两种发送数据给客户端的方法:

作为处理消息或处理订阅的附带结果;

使用消息模板。

我们已经了解了一些处理消息和处理订阅的方法,所以首先看一下如何通过这些方法发送消息给客户端。然后,再看一下Spring的SimpMessagingTemplate,它能够在应用的任何地方发送消息。

在处理消息之后,发送消息

程序清单18.6中,handleShout()只是简单地返回void。它的任务就是处理消息,并不需要给客户端回应。

如果你想要在接收消息的时候,同时在响应中发送一条消息,那么需要做的仅仅是将内容返回就可以了,方法签名不再是使用void。例如,如果你想发送“Polo!”消息作为“Marco!”消息的回应,那么只需将handleShout()修改为如下所示:

在这个新版本的handleShout()方法中,会返回一个新的Shout对象。通过简单地返回一个对象,处理器方法同时也变成了发送方法。当@MessageMapping注解标示的方法有返回值的时候,返回的对象将会进行转换(通过消息转换器)并放到STOMP帧的负载中,然后发送给消息代理。

默认情况下,帧所发往的目的地会与触发处理器方法的目的地相同,只不过会添加上“/topic”前缀。就本例而言,这意味着handleShout()方法所返回的Shout对象会写入到STOMP帧的负载中,并发布到“/topic/marco”目的地。不过,我们可以通过为方法添加@SendTo注解,重载目的地:

按照这个@SendTo注解,消息将会发布到“/topic/shout”。所有订阅这个主题的应用(如客户端)都会收到这条消息。

这样的话,handleShout()在收到一条消息的时候,作为响应也会发送一条消息。按照类似的方式,@SubscribeMapping注解标注的方式也能发送一条消息,作为订阅的回应。例如,通过为控制器添加如下的方法,当客户端订阅的时候,将会发送一条Shout信息:

这里的@SubscribeMapping注解表明当客户端订阅“/app/marco”(“/app”是应用目的地的前缀)目的地的时候,将会调用handleSubscription()方法。它所返回的Shout对象将会进行转换并发送回客户端。

@SubscribeMapping的区别在于这里的Shout消息将会直接发送给客户端,而不必经过消息代理。如果你为方法添加@SendTo注解的话,那么消息将会发送到指定的目的地,这样会经过代理。

在应用的任意地方发送消息

@MessageMapping和@SubscribeMapping提供了一种很简单的方式来发送消息,这是接收消息或处理订阅的附带结果。不过,Spring的SimpMessagingTemplate能够在应用的任何地方发送消息,甚至不必以首先接收一条消息作为前提。

使用SimpMessagingTemplate的最简单方式是将它(或者其接口SimpMessage-SendingOperations)自动装配到所需的对象中。

为了将这一切付诸实施,我们重新看一下Spittr的首页,为其提供实时的Spittle feed功能。按照其当前的写法,控制器会处理首页的请求,将最新的Spittle列表获取到,并将其放到模型中,然后渲染到用户的浏览器中。尽管这样运行起来也不错,但是它并没有提供Spittle更新的实时feed。如果用户想要看一个更新的Spittle feed,那必须要在浏览器中刷新页面。

我们不必要求用户刷新页面,而是让首页订阅一个STOMP主题,在Spittle创建的时候,该主题能够收到Spittle更新的实时feed。在首页中,我们需要添加如下的JavaScript代码块:

与之前的样例一样,我们首先创建了SockJS实例,然后基于该SockJS实例创建了Stomp实例。在连接到STOMP代理之后,我们订阅了“/topic/spittlefeed”,并指定当消息达到的时候,由handleSpittle()函数来处理Spittle更新。handleSpittle()函数会将传入的消息体解析为对应的JavaScript对象,然后使用Handlebars库将Spittle数据渲染为HTML并插入到列表中。Handlebars模板定义在一个单独的<script>标签中,如下所示:

在服务器端,我们可以使用SimpMessagingTemplate将所有新创建的Spittle以消息的形式发布到“/topic/spittlefeed”主题上。如下程序清单展现的SpittleFeedServiceImpl就是实现该功能的简单服务:

程序清单18.8 SimpMessagingTemplate能够在应用的任何地方发布消息

配置Spring支持STOMP的一个副作用就是在Spring应用上下文中已经包含了SimpMessagingTemplate。因此,我们在这里没有必要再创建新的实例。Spittle-FeedServiceImpl的构造器使用了@Autowired注解,这样当创建SpittleFeedService-Impl的时候,就能注入SimpMessagingTemplate(以SimpMessageSendingOperations的形式)了。

发送Spittle消息的地方在broadcastSpittle()方法中。它在注入的SimpMessageSendingOperations上调用了convertAndSend()方法,将Spittle转换为消息,并将其发送到“/topic/spittlefeed”主题上。如果你觉得convertAndSend()方法看起来很眼熟的话,那是因为它模拟了JmsTemplate和RabbitTemplate所提供的同名方法。

不管我们通过convertAndSend()方法,还是借助处理器方法的结果,在发布消息给STOMP主题的时候,所有订阅该主题的客户端都会收到消息。在这个场景下,我们希望所有的客户端都能及时看到实时的Spittle feed,这种做法是很好的。但有的时候,我们希望发送消息给指定的用户,而不是所有的客户端。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文