web-sockets.md 27.3 KB
Newer Older
dallascao's avatar
dallascao 已提交
1 2
# WebSockets 支持

3
## WebSockets 支持
dallascao's avatar
dallascao 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41

从版本 4.1 开始, Spring 集成有 WebSocket 支持。它基于 Spring 框架的`web-socket`模块中的体系结构、基础设施和 API。因此, Spring  WebSocket 的许多组件(例如`SubProtocolHandler``WebSocketClient`)和配置选项(例如`@EnableWebSocketMessageBroker`)可以在 Spring 集成内重用。有关更多信息,请参见 Spring 框架参考手册中的[Spring Framework WebSocket Support](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket)章节。

你需要在项目中包含此依赖项:

Maven

```
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-websocket</artifactId>
    <version>5.5.9</version>
</dependency>
```

Gradle

```
compile "org.springframework.integration:spring-integration-websocket:5.5.9"
```

对于服务器端,必须显式地包含`org.springframework:spring-webmvc`依赖项。

Spring 框架 WebSocket 基础设施是基于 Spring 消息传递基础并且提供了基于与 Spring 集成使用的相同的`MessageChannel`实现和`MessageHandler`实现(以及一些 POJO-method 注释映射)的基本消息传递框架。因此, Spring 集成可以直接参与到 WebSocket 流中,即使没有 WebSocket 适配器也是如此。为此,你可以使用适当的注释来配置 Spring 集成`@MessagingGateway`,如下例所示:

```
@MessagingGateway
@Controller
public interface WebSocketGateway {

    @MessageMapping("/greeting")
    @SendToUser("/queue/answer")
    @Gateway(requestChannel = "greetingChannel")
    String greeting(String payload);

}
```

42
### 概述
dallascao's avatar
dallascao 已提交
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

由于 WebSocket 协议根据定义是流的并且我们可以同时向 WebSocket 发送和接收消息,因此我们可以处理适当的`WebSocketSession`,而不管是在客户端还是服务器端。为了封装连接管理和`WebSocketSession`注册中心,`IntegrationWebSocketContainer`提供了`ClientWebSocketContainer``ServerWebSocketContainer`实现。由于[WebSocket API](https://www.jcp.org/en/jsr/detail?id=356)及其在 Spring 框架中的实现(具有许多扩展),在服务器端和客户端都使用了相同的类(当然,从 Java 的角度来看)。因此,大多数连接和`WebSocketSession`注册表选项在两边都是相同的。这使我们能够重用许多配置项和基础设施挂钩,以便在服务器端和客户端构建 WebSocket 应用程序。下面的示例展示了组件如何实现这两个目的:

```
//Client side
@Bean
public WebSocketClient webSocketClient() {
    return new SockJsClient(Collections.singletonList(new WebSocketTransport(new JettyWebSocketClient())));
}

@Bean
public IntegrationWebSocketContainer clientWebSocketContainer() {
    return new ClientWebSocketContainer(webSocketClient(), "ws://my.server.com/endpoint");
}

//Server side
@Bean
public IntegrationWebSocketContainer serverWebSocketContainer() {
    return new ServerWebSocketContainer("/endpoint").withSockJs();
}
```

WebSocket 该被设计用于实现双向消息传递,并且可以在入站和出站通道适配器之间共享(见下文),在使用单向消息传递时可以仅从其中一个引用 WebSocket。它可以在没有任何通道适配器的情况下使用,但是,在这种情况下,`IntegrationWebSocketContainer`仅作为`WebSocketSession`注册中心发挥作用。

|   |`ServerWebSocketContainer`实现`WebSocketConfigurer`将一个内部`IntegrationWebSocketContainer.IntegrationWebSocketHandler`注册为`Endpoint`<br/>它在提供的`paths`下这样做和其他服务器 WebSocket 选项(例如`HandshakeHandler``SockJS fallback`)内的`ServletWebSocketHandlerRegistry`用于目标供应商 WebSocket 容器。<br/>该注册是通过一个基础设施`WebSocketIntegrationConfigurationInitializer`组件实现的,其执行与`@EnableWebSocket`注释相同的操作。<br/>这意味着,通过使用`@EnableIntegration`(或应用程序上下文中的任何 Spring 集成名称空间),你可以省略`@EnableWebSocket`声明,因为 Spring 集成基础结构检测所有 WebSocket 端点。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

70
###  WebSocket 入站通道适配器
dallascao's avatar
dallascao 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

`WebSocketInboundChannelAdapter`实现了`WebSocketSession`交互的接收部分。你必须为它提供`IntegrationWebSocketContainer`,并且适配器将自身注册为`WebSocketListener`以处理传入消息和`WebSocketSession`事件。

|   |只有一个`WebSocketListener`可以在`IntegrationWebSocketContainer`中注册。|
|---|--------------------------------------------------------------------------------------|

对于 WebSocket 子协议,`WebSocketInboundChannelAdapter`可以配置`SubProtocolHandlerRegistry`作为第二个构造函数参数。适配器委托给`SubProtocolHandlerRegistry`,以确定适用于已接受的`WebSocketSession``SubProtocolHandler`,并根据子协议实现将`WebSocketMessage`转换为`Message`

|   |默认情况下,`WebSocketInboundChannelAdapter`仅依赖于 RAW`PassThruSubProtocolHandler`实现,该实现将`WebSocketMessage`转换为`Message`。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

`WebSocketInboundChannelAdapter`只接受并发送到底层集成流`Message`实例,这些实例具有`SimpMessageType.MESSAGE`或空的`simpMessageType`头。所有其他`Message`类型都是通过`ApplicationEvent`实现发出的`SubProtocolHandler`实例来处理的(例如`StompSubProtocolHandler`)。

在服务器端,如果存在`@EnableWebSocketMessageBroker`配置,则可以使用`useBroker = true`选项配置`WebSocketInboundChannelAdapter`。在这种情况下,所有`non-MESSAGE``Message`类型都被委托给所提供的`AbstractBrokerMessageHandler`。此外,如果代理中继配置了目标前缀,那么那些匹配代理目标的消息将路由到`AbstractBrokerMessageHandler`,而不是`WebSocketInboundChannelAdapter``outputChannel`

如果`useBroker = false`并且接收到的消息是`SimpMessageType.CONNECT`类型的,则`WebSocketInboundChannelAdapter`立即向`SimpMessageType.CONNECT_ACK`消息发送`WebSocketSession`,而不将其发送到信道。

|   |Spring 的 WebSocket 支持只允许配置一个代理中继。<br/>因此,我们不需要`AbstractBrokerMessageHandler`引用。<br/>它是在应用程序上下文中检测到的。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

有关更多配置选项,请参见[WebSockets 名称空间支持](#web-sockets-namespace)

93
###  WebSocket 出站通道适配器
dallascao's avatar
dallascao 已提交
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110

the`WebSocketOutboundChannelAdapter`:

1. 接受 Spring 来自其`MessageChannel`的集成消息

2.`MessageHeaders`中确定`WebSocketSession``id`

3. 从提供的`IntegrationWebSocketContainer`检索`WebSocketSession`

4. 从提供的`SubProtocolHandlerRegistry``WebSocketMessage`工作的转换和发送委托给相应的`SubProtocolHandler`

在客户端,`WebSocketSession``id`消息头不是必需的,因为`ClientWebSocketContainer`仅处理单个连接及其`WebSocketSession`分别。

要使用 STOMP 子协议,你应该使用`StompSubProtocolHandler`配置此适配器。然后,你可以使用`StompHeaderAccessor.create(StompCommand…​)``MessageBuilder`向此适配器发送任何 Stomp 消息类型,或者只使用`HeaderEnricher`(参见[页眉 Enricher](./content-enrichment.html#header-enricher))。

本章的其余部分主要介绍了附加的配置选项。

111
### WebSockets 名称空间支持
dallascao's avatar
dallascao 已提交
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131

Spring 集成 WebSocket 命名空间包括在本章的其余部分中描述的几个组件。要将其包含在配置中,请在应用程序上下文配置文件中使用以下名称空间声明:

```
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:int="http://www.springframework.org/schema/integration"
  xmlns:int-websocket="http://www.springframework.org/schema/integration/websocket"
  xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    https://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/websocket
    https://www.springframework.org/schema/integration/websocket/spring-integration-websocket.xsd">
    ...
</beans>
```

132
#### `<int-websocket:client-container>`属性
dallascao's avatar
dallascao 已提交
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164

下面的清单显示了`<int-websocket:client-container>`元素可用的属性:

```
<int-websocket:client-container
                  id=""                        (1)
                  client=""                    (2)
                  uri=""                       (3)
                  uri-variables=""             (4)
                  origin=""                    (5)
                  send-time-limit=""           (6)
                  send-buffer-size-limit=""    (7)
                  auto-startup=""              (8)
                  phase="">                    (9)
                <int-websocket:http-headers>
                  <entry key="" value=""/>
                </int-websocket:http-headers>  (10)
</int-websocket:client-container>
```

|**1** |组件 Bean 名称。|
|------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2** |参考文献`WebSocketClient` Bean。|
|**3** |将`uri``uriTemplate`发送到目标 WebSocket 服务。<br/>如果将其用作带有 URI 变量占位符的`uriTemplate`,则需要`uri-variables`属性。|
|**4** |<br/>属性值内的 URI 变量占位符的逗号分隔的值。<br/>这些值根据它们在`uri`中的顺序被替换到占位符中。<br/>参见[`UriComponents.expand(Object…​uriVariableValues)`(https://DOCS. Spring.io/ Spring//DOCS/current/javadoc-api/org/SpringFramework/web/util/utilComponents.html expand-uribot)。|
|**5** |`Origin`握手 HTTP 标头值。|
|**6** |WebSocket 会话“发送”超时限制。<br/>默认为`10000`。|
|**7** |WebSocket 会话“发送”消息大小限制。<br/>默认为`524288`。|
|**8** |布尔值,表示此端点是否应自动启动。<br/>默认为`false`,假设此容器是从[WebSocket inbound adapter](#web-socket-inbound-adapter)启动的。|
|**9** |这个端点应该在其中开始和停止的生命周期阶段。<br/>值越低,这个端点开始得越早,停止得越晚。<br/>默认值是`Integer.MAX_VALUE`<br/>值可以是负数。<br/>参见[`SmartLifeCycle`(https:/DOCS. Spring.io/ Spring//DOCS/current/javadoc-api/org/api/context/smartlifycle.html)。|
|**10**|a`Map`of`HttpHeaders`用于握手请求。|

165
#### `<int-websocket:server-container>`属性
dallascao's avatar
dallascao 已提交
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

下面的清单显示了`<int-websocket:server-container>`元素可用的属性:

```
<int-websocket:server-container
          id=""                         (1)
          path=""                       (2)
          handshake-handler=""          (3)
          handshake-interceptors=""     (4)
          decorator-factories=""        (5)
          send-time-limit=""            (6)
          send-buffer-size-limit=""     (7)
          allowed-origins="">           (8)
          <int-websocket:sockjs
            client-library-url=""       (9)
            stream-bytes-limit=""       (10)
            session-cookie-needed=""    (11)
            heartbeat-time=""           (12)
            disconnect-delay=""         (13)
            message-cache-size=""       (14)
            websocket-enabled=""        (15)
            scheduler=""                (16)
            message-codec=""            (17)
            transport-handlers=""       (18)
            suppress-cors="true"="" />  (19)
</int-websocket:server-container>
```

|**1** |组件 Bean 名称。|
|------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2** |一种将特定请求映射到`WebSocketHandler`的路径(或以逗号分隔的路径)。<br/>支持精确的路径映射 URI(例如`/myPath`)和 Ant 样式的路径模式(例如`/myPath/**`)。|
|**3** |`HandshakeHandler` Bean 引用.<br/>默认为`DefaultHandshakeHandler`。|
|**4** |参考文献列表`HandshakeInterceptor` Bean。|
|**5** |用于修饰用于处理 WebSocket 消息的处理程序的一个或多个工厂的列表(`WebSocketHandlerDecoratorFactory`)。<br/>这对于某些高级用例(例如,以允许 Spring 安全性在相应的 HTTP会话过期时强制关闭 WebSocket 会话。有关更多信息,请参见。|
|**6** |请参阅[`<int-websocket:client-container>`](# WebSocket-client-container-attributes)上的相同选项。|
|**7** |请参阅[`<int-websocket:client-container>`](# WebSocket-client-container-attributes)上的相同选项。|
|**8** |允许的源头值。<br/>你可以将多个源代码指定为逗号分隔的列表。<br/>此检查主要是为浏览器客户端设计的。<br/>没有什么可以阻止其他类型的客户端修改源头值。<br/>当启用 Sockjs 并且限制允许的源代码时,不使用起源头来处理跨起源请求(`jsonp-polling``iframe-xhr-polling``iframe-eventsource`,和`iframe-htmlfile`)的传输类型将被禁用。因此,不支持 IE6 和 IE7,并且只支持 IE8 和 IE9 而不支持 cookie。<br/>默认情况下,所有起源都是允许的。|
|**9** |没有本机跨域通信的传输(例如`eventsource``htmlfile`)必须在不可见的 iframe 中从“foreign”域获得一个简单的页面,以便 IFRAME 中的代码可以从本地域运行到 Sockjs 服务器。<br/>由于 IFRAME 需要加载 Sockjs JavaScript 客户库,因此该属性允许你指定加载它的位置。<br/>默认情况下,它指向`[https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js](https://d1fxtkz8shb9d2.cloudfront.net/sockjs-0.3.4.min.js)`。,但是,<br/>,还可以将它设置为指向应用程序提供的 URL。<br/>注意,可以指定一个相对 URL,在这种情况下,URL 必须与 IFRAME URL 相对。<br/>,例如,假设一个 Sockjs 端点映射到`/sockjs`,并且得到的 iFrame URL 是`/sockjs/iframe.html`,那么相对 URL 必须以“.././”开头,才能遍历到 Sockjs 映射上方的位置。<br/>对于基于前缀的 Servlet 映射,你可能需要再遍历一次。|
|**10**|在关闭单个 HTTP 流请求之前,可以通过该请求发送的最小字节数。<br/>默认为`128K`(即 128\*1024 或 131072 字节)。|
|**11**|来自 Sockjs 的响应中的`cookie_needed``/info`Endpoint.<br/>此属性指示应用程序是否需要`JSESSIONID`cookie 才能正常工作(例如,用于负载平衡或在 Java Servlet 容器中使用 HTTP会话)。|
|**12**|当服务器没有发送任何消息时的时间量(以毫秒为单位),在此之后服务器应该<br/>向客户端发送心跳帧,以防止连接中断。<br/>默认值为`25,000`(25 秒)。|
|**13**|客户机在没有接收连接(即服务器可以在其上向客户机发送数据的活动连接)之后被认为断开连接之前的时间量(以毫秒为单位)。<br/>默认值为`5000`。|
|**14**|会话在等待来自客户端的下一个 HTTP 轮询请求时可以缓存的服务器到客户端消息的数量。<br/>默认大小为`100`。|
|**15**|有些负载均衡器不支持 WebSockets。<br/>将此选项设置为`false`,以禁用服务器端的 WebSocket 传输。<br/>默认值为`true`。|
|**16**|`TaskScheduler` Bean 引用。<br/>如果不提供值,将创建一个新的`ThreadPoolTaskScheduler`实例。<br/>此调度程序实例用于调度心跳消息。|
|**17**|该 Bean 引用用于编码和解码 Sockjs 消息。默认情况下,使用,这要求在 Classpath 上存在 Jackson 库。|
|**18**|参考文献列表`TransportHandler` Bean。|
|**19**|是否禁用自动添加 SOCKJS 请求的 CORS 头。<br/>默认值为`false`。|

215
#### `<int-websocket:outbound-channel-adapter>`属性
dallascao's avatar
dallascao 已提交
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

下面的清单显示了`<int-websocket:outbound-channel-adapter>`元素可用的属性:

```
<int-websocket:outbound-channel-adapter
                          id=""                             (1)
                          channel=""                        (2)
                          container=""                      (3)
                          default-protocol-handler=""       (4)
                          protocol-handlers=""              (5)
                          message-converters=""             (6)
                          merge-with-default-converters=""  (7)
                          auto-startup=""                   (8)
                          phase=""/>                        (9)
```

|**1**|组件 Bean name.<br/>如果不提供`channel`属性,则在应用程序上下文中创建并注册一个`DirectChannel`,并将该`id`属性作为 Bean 名称。<br/>在这种情况下,端点以 Bean 名称`id`加上`.adapter`注册。<br/>`MessageHandler`则以 Bean 别名`id`加上`.handler`注册。|
|-----|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|标识连接到此适配器的通道。|
|**3**|对`IntegrationWebSocketContainer` Bean 的引用,它封装了低级连接和`WebSocketSession`处理操作。<br/>需要。|
|**4**|对`SubProtocolHandler`实例的可选引用。<br/>当客户端未请求子协议或它是单个协议处理程序时使用。<br/>如果未提供此引用或`protocol-handlers`列表,则默认使用`PassThruSubProtocolHandler`。|
|**5**|该通道适配器的`SubProtocolHandler` Bean 引用列表。<br/>如果只提供单个 Bean 引用而不提供`default-protocol-handler`,则该单个`SubProtocolHandler`用作`default-protocol-handler`<br/>如果不设置此属性或`default-protocol-handler`,默认情况下使用`PassThruSubProtocolHandler`。|
|**6**|此通道适配器的`MessageConverter` Bean 引用列表。|
|**7**|布尔值,表示是否应在任何自定义转换器之后注册默认转换器。<br/>只有在提供`message-converters`时才使用此标志。<br/>否则,将注册所有默认转换器。<br/>默认为`false`<br/>默认转换器(按顺序排列):`StringMessageConverter``ByteArrayMessageConverter`,和(如果 Jackson 库存在于 Classpath 上)。|
|**8**|布尔值,表示此端点是否应自动启动。<br/>默认为`true`。|
|**9**|这个端点应该在其中开始和停止的生命周期阶段。<br/>值越低,这个端点开始得越早,停止得越晚。<br/>默认值是`Integer.MIN_VALUE`<br/>值可以是负数。<br/>参见[`SmartLifeCycle`(https:/DOCS. Spring.io/ Spring/DOCS/current/javadoc-apf/org/api/context/smartlifycle.html)。|

243
#### `<int-websocket:inbound-channel-adapter>`属性
dallascao's avatar
dallascao 已提交
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278

下面的清单显示了`<int-websocket:outbound-channel-adapter>`元素可用的属性:

```
<int-websocket:inbound-channel-adapter
                            id=""  (1)
                            channel=""  (2)
                            error-channel=""  (3)
                            container=""  (4)
                            default-protocol-handler=""  (5)
                            protocol-handlers=""  (6)
                            message-converters=""  (7)
                            merge-with-default-converters=""  (8)
                            send-timeout=""  (9)
                            payload-type=""  (10)
                            use-broker=""  (11)
                            auto-startup=""  (12)
                            phase=""/>  (13)
```

|**1** |组件 Bean name.<br/>如果不设置`channel`属性,则在应用程序上下文中创建并注册一个`DirectChannel`,并将该`id`属性作为 Bean 名称。<br/>在本例中,端点以 Bean 名称`id`加上`.adapter`进行注册。|
|------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2** |标识连接到此适配器的通道。|
|**3** |应该发送`ErrorMessage`实例的`MessageChannel` Bean 引用。|
|**4** |请参阅[`<int-websocket:outbound-channel-adapter>`](# WebSocket-出站-通道-适配器-属性)上的相同选项。|
|**5** |请参阅[`<int-websocket:outbound-channel-adapter>`](# WebSocket-出站-通道-适配器-属性)上的相同选项。|
|**6** |请参阅[`<int-websocket:outbound-channel-adapter>`](# WebSocket-出站-通道-适配器-属性)上的相同选项。|
|**7** |请参阅[`<int-websocket:outbound-channel-adapter>`](# WebSocket-出站-通道-适配器-属性)上的相同选项。|
|**8** |请参阅[`<int-websocket:outbound-channel-adapter>`](# WebSocket-出站-通道-适配器-属性)上的相同选项。|
|**9** |如果信道可以阻塞,则在向信道发送消息时等待的最大时间量(以毫秒为单位)。<br/>例如,如果已达到其最大容量,则`QueueChannel`可以阻塞直到可用空间。|
|**10**|用于从传入的`WebSocketMessage`转换为目标`payload`的完全限定的 Java 类型名称。<br/>默认为`java.lang.String`。|
|**11**|指示此适配器是否从应用程序上下文向`AbstractBrokerMessageHandler`发送带有代理目的地的`AbstractBrokerMessageHandler`实例和消息。<br/>当此属性为`true`时,需要`Broker Relay`配置。<br/>此属性仅在服务器端使用。<br/>在客户端,它被忽略。<br/>默认为`false`。|
|**12**|请参阅[`<int-websocket:outbound-channel-adapter>`](# WebSocket-出站-通道-适配器-属性)上的相同选项。|
|**13**|参见[`<int-websocket:outbound-channel-adapter>`](# WebSocket-出站-通道-适配器-属性)上的相同选项。|

279
### 使用`ClientStompEncoder`
dallascao's avatar
dallascao 已提交
280 281 282

从版本 4.3.13 开始, Spring 集成提供了`ClientStompEncoder`(作为标准`StompEncoder`的扩展),用于在 WebSocket 通道适配器的客户端上使用。为了进行正确的客户端消息准备,你必须将`ClientStompEncoder`的一个实例注入`StompSubProtocolHandler`中。默认`StompSubProtocolHandler`的一个问题是,它是为服务器端设计的,因此它将`SEND``stompCommand`报头更新为`MESSAGE`(根据服务器端的 stomp 协议的要求)。如果客户机没有在适当的`SEND`Web 套接字框架中发送消息,则某些 STOMP 代理不接受它们。在这种情况下,`ClientStompEncoder`的目的是覆盖`stompCommand`头并将其设置为`SEND`值,然后将消息编码为`byte[]`

283
### 动态 WebSocket 端点注册
dallascao's avatar
dallascao 已提交
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320

从版本 5.5 开始, WebSocket 服务器端点(基于`ServerWebSocketContainer`的通道适配器)现在可以在运行时注册(并删除)-映射的`paths`a`ServerWebSocketContainer`通过`HandlerMapping`公开到`DispatcherServlet`中,并可供 WebSocket 客户端访问。[动态和运行时集成流](./dsl.html#java-dsl-runtime-flows)支持有助于以透明的方式注册这些端点:

```
@Autowired
IntegrationFlowContext integrationFlowContext;

@Autowired
HandshakeHandler handshakeHandler;
...
ServerWebSocketContainer serverWebSocketContainer =
       new ServerWebSocketContainer("/dynamic")
               .setHandshakeHandler(this.handshakeHandler);

WebSocketInboundChannelAdapter webSocketInboundChannelAdapter =
       new WebSocketInboundChannelAdapter(serverWebSocketContainer);

QueueChannel dynamicRequestsChannel = new QueueChannel();

IntegrationFlow serverFlow =
       IntegrationFlows.from(webSocketInboundChannelAdapter)
               .channel(dynamicRequestsChannel)
               .get();

IntegrationFlowContext.IntegrationFlowRegistration dynamicServerFlow =
       this.integrationFlowContext.registration(serverFlow)
               .addBean(serverWebSocketContainer)
               .register();
...
dynamicServerFlow.destroy();
```

|   |在动态流注册中调用`.addBean(serverWebSocketContainer)``ServerWebSocketContainer`的实例添加到`ApplicationContext`中以进行端点注册是很重要的,<br/>当动态流注册被破坏时,相关的`ServerWebSocketContainer`实例也被破坏,以及相应的端点注册也被破坏,包括 URL 路径映射。|
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |动态 WebSocket 端点只能通过 Spring 集成机制进行注册:当使用常规 Spring 时, Spring 集成配置退出,并且不注册用于动态端点的基础设施。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|