- 作者简介
- 内容提要
- 关于本书
- 路线图
- 代码规范与下载
- 作者在线
- 封面插图简介
- 前言
- 译者序
- 致谢
- 第1部分 Spring 的核心
- 第1章 Spring 之旅
- 第2章 装配 Bean
- 第3章 高级装配
- 第4章 面向切面的 Spring
- 第2部分 Web 中的 Spring
- 第5章 构建 Spring Web 应用程序
- 第6章 渲染 Web 视图
- 第7章 Spring MVC 的高级技术
- 第8章 使用 Spring Web Flow
- 第9章 保护 Web 应用
- 第3部分 后端中的 Spring
- 第10章 通过 Spring 和 JDBC 征服数据库
- 第11章 使用对象-关系映射持久化数据
- 第12章 使用 NoSQL 数据库
- 第13章 缓存数据
- 第14章 保护方法应用
- 第4部分 Spring 集成
- 第15章 使用远程服务
- 第16章 使用 Spring MVC 创建 REST API
- 第17章 Spring消息
- 第18章 使用 WebSocket 和 STOMP 实现消息功能
- 第19章 使用 Spring 发送 Email
- 第20章 使用 JMX 管理 Spring Bean
- 第21章 借助 Spring Boot 简化 Spring 开发
18.3.3 发送消息到客户端
到目前为止,客户端负责了所有的消息发送,服务器只能监听这些消息。对于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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论