spring-messaging 和 spring-websocket 模块中提供了 STOMP over WebSocket 支持。一旦你有了这些依赖,你就可以通过 SockJS Fallback 在WebSocket上暴露 STOMP 端点,如下例所示:

先添加依赖:

  1. // spring websocket 支持
  2. implementation 'org.springframework:spring-websocket:5.3.15'
  3. // spring-messaging 可以提供 STOMP 支持
  4. implementation 'org.springframework:spring-messaging:5.3.15'

暴露 STOMP 端点

  1. import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
  2. import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
  3. @Configuration
  4. @EnableWebSocketMessageBroker
  5. public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
  6. @Override
  7. public void registerStompEndpoints(StompEndpointRegistry registry) {
  8. // /portfolio 是 WebSocket(或 SockJS)的端点的 HTTP URL。客户端需要连接以进行 WebSocket 握手。
  9. registry.addEndpoint("/portfolio").withSockJS();
  10. }
  11. @Override
  12. public void configureMessageBroker(MessageBrokerRegistry config) {
  13. // 目标标头以 /app 开头的 STOMP 消息会被路由到 @Controller 类中的 @MessageMapping 方法。
  14. config.setApplicationDestinationPrefixes("/app");
  15. // 使用内置的消息代理进行订阅和广播,并且 将目标标头以 /topic 或 /queue 开头的消息路由到代理。
  16. config.enableSimpleBroker("/topic", "/queue");
  17. }
  18. }

下面是等效的 XML 配置:

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:websocket="http://www.springframework.org/schema/websocket"
  4. xsi:schemaLocation="
  5. http://www.springframework.org/schema/beans
  6. https://www.springframework.org/schema/beans/spring-beans.xsd
  7. http://www.springframework.org/schema/websocket
  8. https://www.springframework.org/schema/websocket/spring-websocket.xsd">
  9. <websocket:message-broker application-destination-prefix="/app">
  10. <websocket:stomp-endpoint path="/portfolio">
  11. <websocket:sockjs/>
  12. </websocket:stomp-endpoint>
  13. <websocket:simple-broker prefix="/topic, /queue"/>
  14. </websocket:message-broker>
  15. </beans>

:::info 对于内置的简单代理,/topic 和 /queue 前缀没有任何特殊含义。它们只是一个惯例,用来区分 pub-sub 和 point-to-point 的消息传递(即许多订阅者和一个消费者)。当你使用外部代理时,请检查该代理的 STOMP 页面,以了解它支持什么样的 STOMP 目的地和前缀。 :::

要从浏览器连接,对于 SockJS,你可以使用 sockjs-client。对于 STOMP,许多应用程序都使用了 jmesnil/stomp-websocket 库(也称为stomp.js),该库功能完整,已在生产中使用多年,但已不再维护。目前,JSteunou/webstomp-client 是该库最积极维护和发展的后继者。下面的示例代码是基于它的:

  1. var socket = new SockJS("/spring-websocket-portfolio/portfolio");
  2. var stompClient = webstomp.over(socket);
  3. stompClient.connect({}, function(frame) {
  4. })

另外,如果你通过 WebSocket(没有 SockJS)连接,你可以使用以下代码:

  1. var socket = new WebSocket("/spring-websocket-portfolio/portfolio");
  2. var stompClient = Stomp.over(socket);
  3. stompClient.connect({}, function(frame) {
  4. })

请注意,前面的例子中的 stompClient 不需要指定登录和密码头文件。即使它这样做了,它们在服务器端也会被忽略(或者说,被覆盖)。关于认证的更多信息,请参见 连接到 Broker认证

更多的示例代码见:

一个例子

本章内容虽然描述得还算清楚,但是在实际实现的过程中,发现还是有一些小坑,不知道是否是由于我的环境对不上的问题。

后端还在在 前面的例子 基础上修改。将 webSocket 的配置改成了如下方式

  1. package cn.mrcode.study.springdocsread.websocket;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.messaging.simp.config.MessageBrokerRegistry;
  5. import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
  6. import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
  7. import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
  8. import org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy;
  9. /**
  10. * @author mrcode
  11. */
  12. @Configuration
  13. @EnableWebSocketMessageBroker
  14. public class MyWebSocketConfig implements WebSocketMessageBrokerConfigurer {
  15. @Override
  16. public void registerStompEndpoints(StompEndpointRegistry registry) {
  17. // portfolio 是 WebSocket(或 SockJS)的端点的 HTTP URL。客户端需要连接以进行 WebSocket 握手。
  18. registry.addEndpoint("/portfolio")
  19. .setAllowedOriginPatterns("*") // 注意跨域设置
  20. .withSockJS();
  21. }
  22. @Override
  23. public void configureMessageBroker(MessageBrokerRegistry config) {
  24. // 目标标头以 /app 开头的 STOMP 消息会被路由到 @Controller 类中的 @MessageMapping 方法。
  25. config.setApplicationDestinationPrefixes("/app");
  26. // 使用内置的消息代理进行订阅和广播,并且 将目标标头以 /topic 或 /queue 开头的消息路由到代理。
  27. config.enableSimpleBroker("/topic", "/queue");
  28. }
  29. /**
  30. * spring 为了支持每种容器自己的 websocket 升级策略,抽象了 RequestUpgradeStrategy,
  31. * <p>对 tomcat 提供了 TomcatRequestUpgradeStrategy 策略</p>
  32. * 如果不申明这个,就会在启动的时候抛出异常:No suitable default RequestUpgradeStrategy found
  33. */
  34. @Bean
  35. public TomcatRequestUpgradeStrategy tomcatRequestUpgradeStrategy() {
  36. return new TomcatRequestUpgradeStrategy();
  37. }
  38. }

前端由于 JSteunou/webstomp-client 没有提供直接的 cdn 相关的 js 文件引用,只能使用 npm 的方式先下载,然后引用

  1. npm install webstomp-client
  2. 然后在 html 中引用,这里你可以想成是,通过 npm install 下载之后,将 webstomp.min.js 拷贝到自己的项目中引用
  3. <script type="text/javascript" src="node_modules/webstomp-client/dist/webstomp.min.js"></script>

下面是 html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>STOMP</title>
  6. </head>
  7. <body>
  8. </body>
  9. <script type="text/javascript" src="/node_modules/webstomp-client/dist/webstomp.min.js"></script>
  10. <script type="text/javascript" src="https://cdnjs.loli.net/ajax/libs/sockjs-client/1.6.0/sockjs.js"></script>
  11. <script>
  12. // 这里使用 sockJs 库链接
  13. var socket = new SockJS("http://localhost:8080/portfolio");
  14. // 文章上说可以使用 WebSocket 链接。 实际上我这里测试不可以,会报错
  15. // var socket = new WebSocket("ws://localhost:8080/portfolio");
  16. var stompClient = webstomp.over(socket);
  17. stompClient.connect({}, function(frame) {
  18. console.log(frame)
  19. })
  20. </script>
  21. </html>

打开该页面后,就能看到控制台输出如下的信息
image.png