- 作者简介
- 内容提要
- 关于本书
- 路线图
- 代码规范与下载
- 作者在线
- 封面插图简介
- 前言
- 译者序
- 致谢
- 第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.2 处理来自客户端的 STOMP 消息
我们在第5章已经学习过,Spring MVC为处理HTTP Web请求提供了面向注解的编程模型。@RequestMapping是Spring MVC中最著名的注解,它会将HTTP请求映射到对请求进行处理的方法上。在第16章,我们也曾经看到相同的编程模型扩展到了RESTful的资源处理中。
STOMP和WebSocket更多的是关于异步消息,与HTTP的请求-响应方式有所不同。但是,Spring提供了非常类似于Spring MVC的编程模型来处理STOMP消息。它非常地相似,以至于对STOMP消息的处理器方法也会包含在带有@Controller注解的类中。
Spring 4.0引入了@MessageMapping注解,它用于STOMP消息的处理,类似于Spring MVC的@RequestMapping注解。当消息抵达某个特定的目的地时,带有@MessageMapping注解的方法能够处理这些消息。例如,考虑如下程序清单中的控制器类。
程序清单18.6 借助@MessageMapping注解能够在控制器中处理STOMP消息
乍一看上去,它非常类似于其他的Spring MVC控制器类。它使用了@Controller注解,所以组件扫描能够找到它并将其注册为bean。就像其他的@Controller类一样,它也包含了处理器方法。
但是这个处理器方法与我们之前看到的有一点区别。handleShout()方法没有使用@RequestMapping注解,而是使用了@MessageMapping注解。这表示handleShout()方法能够处理指定目的地上到达的消息。在本例中,这个目的地也就是“/app/marco”(“/app”前缀是隐含的,因为我们将其配置为应用的目的地前缀)。
因为handleShout()方法接收一个Shout参数,所以Spring的某一个消息转换器会将STOMP消息的负载转换为Shout对象。Shout类非常简单,它是只具有一个属性的JavaBean,包含了消息的内容:
因为我们现在处理的不是HTTP,所以无法使用Spring的HttpMessageConverter实现将负载转换为Shout对象。Spring 4.0提供了几个消息转换器,作为其消息API的一部分。表18.1描述了这些消息转换器,在处理STOMP消息的时候可能会用到它们。
表18.1 Spring能够使用某一个消息转换器将消息负载转换为Java类型
消息转换器 | 描 述 |
ByteArrayMessageConverter | 实现MIME类型为“application/octet-stream”的消息与byte[]之间的相互转换 |
MappingJackson2MessageConverter | 实现MIME类型为“application/json”的消息与Java对象之间的相互转换 |
StringMessageConverter | 实现MIME类型为“text/plain”的消息与String之间的相互转换 |
假设handleShout()方法所处理消息的内容类型为“application/json”(这应该是一个安全的假设,因为Shout不是byte[]和String),MappingJackson2MessageConverter会负责将JSON消息转换为Shout对象。就像在HTTP中对应的MappingJackson2HttpMessageConverter一样,MappingJackson2MessageConverter会将其任务委托给底层的Jackson 2 JSON处理器。默认情况下,Jackson会使用反射将JSON属性映射为Java对象的属性。尽管在本例中没有必要,但是我们可以通过在Java类型上使用Jackson注解,影响具体的转换行为。
处理订阅
除了@MessagingMapping注解以外,Spring还提供了@SubscribeMapping注解。与@MessagingMapping注解方法类似,当收到STOMP订阅消息的时候,带有@SubscribeMapping注解的方法将会触发。
很重要的一点,与@MessagingMapping方法类似,@SubscribeMapping方法也是通过AnnotationMethodMessageHandler接收消息的(如图18.2和图18.3所示)。按照程序清单18.5的配置,这就意味着@SubscribeMapping方法只能处理目的地以“/app”为前缀的消息。
这可能看上去有些诡异,因为应用发出的消息都会经过代理,目的地要以“/topic”或“/queue”打头。客户端会订阅这些目的地,而不会订阅前缀为“/app”的目的地。如果客户端订阅“/topic”和“/queue”这样的目的地,那么@SubscribeMapping方法也就无法处理这样的订阅了。如果是这样的话,@SubscribeMapping有什么用处呢?
@SubscribeMapping的主要应用场景是实现请求-回应模式。在请求-回应模式中,客户端订阅某一个目的地,然后预期在这个目的地上获得一个一次性的响应。
例如,考虑如下@SubscribeMapping注解标注的方法:
可以看到,handleSubscription()方法使用了@SubscribeMapping注解,用这个方法来处理对“/app/marco”目的地的订阅(与@MessageMapping类似,“/app”是隐含的)。当处理这个订阅时,handleSubscription()方法会产生一个输出的Shout对象并将其返回。然后,Shout对象会转换成一条消息,并且会按照客户端订阅时相同的目的地发送回客户端。
如果你觉得这种请求-回应模式与HTTP GET的请求-响应模式并没有太大差别的话,那么你基本上是正确的。但是,这里的关键区别在于HTTP GET请求是同步的,而订阅的请求-回应模式则是异步的,这样客户端能够在回应可用时再去处理,而不必等待。
编写JavaScript客户端
handleShout()方法已经可以处理发送过来的消息了。现在,我们需要的就是发送消息的客户端。
如下的程序清单展现了一些JavaScript客户端代码,它会连接“/marcopolo”端点并发送“Marco!”消息。
程序清单18.7 借助STOMP库,通过JavaScript发送消息
与我们之前的JavaScript客户端样例类似,在这里首先针对给定的URL创建一个SockJS实例。在本例中,URL引用的是程序清单18.5中所配置的STOMP端点(不包括应用的上下文路径“/stomp”)。
但是,这里的区别在于,我们不再直接使用SockJS,而是通过调用Stomp.over(sock)创建了一个STOMP客户端实例。这实际上封装了SockJS,这样就能在WebSocket连接上发送STOMP消息。
接下来,我们使用STOMP进行连接,假设连接成功,然后发送带有JSON负载的消息到名为“/marco”的目的地。往send()方法传递的第二个参数是一个头信息的Map,它会包含在STOMP的帧中,不过在这个例子中,我们没有提供任何参数,Map是空的。
现在,我们有了能够发送消息到服务器的客户端,以及用来处理消息的服务端处理器方法。这是一个好的开端,但是你可能已经发现这都是单向的。接下来,我们让服务器发出的声音,看一下如何发送消息给客户端。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论