# Stomp 支持
## [](#stomp)stomp 支持
Spring 集成版本 4.2 引入了 STOMP(简单文本定向消息传递协议)客户端支持。它基于 Spring 框架的消息传递模块 STOMP 包中的体系结构、基础设施和 API。 Spring 集成使用 Spring Stomp 组件中的许多(例如`StompSession`和`StompClientSupport`)。有关更多信息,请参见 Spring 框架参考手册中的[Spring Framework STOMP Support](https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#websocket-stomp-client)章节。
你需要在项目中包含此依赖项:
Maven
```
org.springframework.integration
spring-integration-stomp
5.5.9
```
Gradle
```
compile "org.springframework.integration:spring-integration-stomp:5.5.9"
```
对于服务器端组件,你需要添加`org.springframework:spring-websocket`和/或`io.projectreactor.netty:reactor-netty`依赖关系。
### [](#stomp-overview)概述
要配置 STOMP,你应该从 STOMP 客户机对象开始。 Spring 框架提供了以下实现方式:
* `WebSocketStompClient`:构建在 Spring WebSocket API 之上,支持标准的 JSR-356 WebSocket、 Jetty 9,以及用于基于 HTTP 的 WebSocket SockJS 仿真的 SockJS 和 SockJS 客户端。
* `ReactorNettyTcpStompClient`:构建于`ReactorNettyTcpClient`项目的`reactor-netty`之上。
你可以提供任何其他`StompClientSupport`实现。参见这些类的[Javadoc](https://docs.spring.io/spring-integration/api/)。
将`StompClientSupport`类设计为*工厂*,以生成针对所提供的`StompSessionHandler`的`StompSession`,并通过*回调*对`StompSessionHandler`和`StompSession`的抽象来完成所有剩余的工作。通过 Spring 集成*适配器*抽象,我们需要提供一些托管共享对象,以将我们的应用程序表示为具有唯一会话的 Stomp 客户机。为此, Spring 集成提供了`StompSessionManager`的抽象,以管理在任意提供的*单身*`StompSession`之间的`StompSessionHandler`。这允许对特定的 Stomp 代理使用*入站*或*出站*通道适配器(或两者)。有关更多信息,请参见`StompSessionManager`(及其实现)Javadocs。
### [](#stomp-inbound-adapter)Stomp 入站通道适配器
`StompInboundChannelAdapter`是一个一站式`MessageProducer`组件,它将你的 Spring 集成应用程序订阅到所提供的 stomp 目的地,并从它们接收消息(通过在连接的`StompSession`上使用所提供的`MessageConverter`从 stomp 帧转换)。通过在`StompInboundChannelAdapter`上使用适当的`@ManagedOperation`注释,你可以在运行时更改目的地(因此也可以更改 stomp 订阅)。
有关更多配置选项,请参见[Stomp 名称空间支持](#stomp-namespace)和`StompInboundChannelAdapter`[Javadoc](https://docs.spring.io/spring-integration/api/org/springframework/integration/stomp/inbound/StompInboundChannelAdapter.html)。
### [](#stomp-outbound-adapter)Stomp 出站通道适配器
`StompMessageHandler`是`MessageHandler`的`MessageHandler`,用于将传出的`Message>`实例通过`destination`发送到 stomp`destination`(在运行时通过 spel 表达式预先配置或确定)(由共享的`StompSession`提供)。
有关更多配置选项,请参见[Stomp 名称空间支持](#stomp-namespace)和`StompMessageHandler`[Javadoc](https://docs.spring.io/spring-integration/api/org/springframework/integration/stomp/outbound/StompMessageHandler.html)。
### [](#stomp-headers)Stomp 标头映射
STOMP 协议提供头作为其框架的一部分。Stomp 框架的整个结构具有以下格式:
```
....
COMMAND
header1:value1
header2:value2
Body^@
....
```
Spring Framework 提供`StompHeaders`来表示这些头。有关更多详细信息,请参见[Javadoc](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/messaging/simp/stomp/StompHeaders.html)。Stomp 帧被转换为和转换为`Message>`实例,这些头被映射为和转换为`MessageHeaders`实例。 Spring 集成为 Stomp 适配器提供了一个默认的`HeaderMapper`实现。实现方式是`StompHeaderMapper`。它分别为入站适配器和出站适配器提供`fromHeaders()`和`toHeaders()`操作。
与许多其他 Spring 集成模块一样,引入了`IntegrationStompHeaders`类来将标准的 stomp 头映射到`MessageHeaders`,并以`stomp_`作为头名称前缀。此外,所有带有该前缀的`MessageHeaders`实例在发送到目标时都映射到`StompHeaders`。
有关更多信息,请参见[Javadoc](https://docs.spring.io/spring-integration/api/)中的这些类和`mapped-headers`属性描述。
### [](#stomp-events)Stomp 集成事件
许多 STOMP 操作是异步的,包括错误处理。例如,Stomp 有一个`RECEIPT`服务器帧,当客户端帧通过添加`RECEIPT`头请求一个服务器帧时,它会返回这个服务器帧。为了提供对这些异步事件的访问, Spring 集成会发出`StompIntegrationEvent`实例,你可以通过实现`ApplicationListener`或使用``(参见[Receiving Spring Application Events](./event.html#appevent-inbound))来获得这些实例。
具体地说,当`stompSessionListenableFuture`由于连接到 Stomp 代理失败而接收到`onFailure()`时,从`AbstractStompSessionManager`发出`StompExceptionEvent`。另一个例子是`StompMessageHandler`。它处理`ERROR`stomp 帧,这些帧是服务器对此`StompMessageHandler`发送的不正确(不接受)消息的响应。
在发送到`StompSession`的消息的异步答案中,`StompMessageHandler`发出`StompReceiptEvent`作为`StompSession.Receiptable`回调的一部分的`StompReceiptEvent`。`StompReceiptEvent`可以是正的,也可以是负的,这取决于`RECEIPT`帧是否在`receiptTimeLimit`期间从服务器接收,你可以在`StompClientSupport`实例上配置该帧。它默认为`15 * 1000`(以毫秒为单位,所以是 15 秒)。
| |只有当要发送的消息的`RECEIPT`stomp 报头不是`null`时,才会添加`StompSession.Receiptable`回调。
你可以分别通过其`StompSession`选项和`StompSessionManager`选项在`StompSession`报头上启用自动`RECEIPT`报头生成。|
|---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
有关如何配置 Spring 集成以接受那些`ApplicationEvent`实例的详细信息,请参见[Stomp 适配器 Java 配置](#stomp-java-config)。
### [](#stomp-java-config)Stomp 适配器 Java 配置
下面的示例展示了用于 Stomp 适配器的全面的 Java 配置:
```
@Configuration
@EnableIntegration
public class StompConfiguration {
@Bean
public ReactorNettyTcpStompClient stompClient() {
ReactorNettyTcpStompClient stompClient = new ReactorNettyTcpStompClient("127.0.0.1", 61613);
stompClient.setMessageConverter(new PassThruMessageConverter());
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.afterPropertiesSet();
stompClient.setTaskScheduler(taskScheduler);
stompClient.setReceiptTimeLimit(5000);
return stompClient;
}
@Bean
public StompSessionManager stompSessionManager() {
ReactorNettyTcpStompSessionManager stompSessionManager = new ReactorNettyTcpStompSessionManager(stompClient());
stompSessionManager.setAutoReceipt(true);
return stompSessionManager;
}
@Bean
public PollableChannel stompInputChannel() {
return new QueueChannel();
}
@Bean
public StompInboundChannelAdapter stompInboundChannelAdapter() {
StompInboundChannelAdapter adapter =
new StompInboundChannelAdapter(stompSessionManager(), "/topic/myTopic");
adapter.setOutputChannel(stompInputChannel());
return adapter;
}
@Bean
@ServiceActivator(inputChannel = "stompOutputChannel")
public MessageHandler stompMessageHandler() {
StompMessageHandler handler = new StompMessageHandler(stompSessionManager());
handler.setDestination("/topic/myTopic");
return handler;
}
@Bean
public PollableChannel stompEvents() {
return new QueueChannel();
}
@Bean
public ApplicationListener stompEventListener() {
ApplicationEventListeningMessageProducer producer = new ApplicationEventListeningMessageProducer();
producer.setEventTypes(StompIntegrationEvent.class);
producer.setOutputChannel(stompEvents());
return producer;
}
}
```
### [](#stomp-namespace)Stomp 名称空间支持
Spring 集成 Stomp 命名空间实现了入站和出站通道适配器组件。要将其包含在配置中,请在应用程序上下文配置文件中提供以下名称空间声明:
```
...
```
#### [](#stomp-outbound-channel-adapter)理解``元素
下面的清单显示了 STOMP 出站通道适配器的可用属性:
```
(9)
```
|**1**|组件 Bean 名.
`MessageHandler`的`id`的 Bean 别名注册为`id`加上`.handler`。
如果不设置`channel`属性,在应用程序上下文中创建并注册了一个`DirectChannel`,并将这个`id`属性的值作为 Bean 名称。
在这种情况下,端点以 Bean 名称`id`加上`.adapter`注册。|
|-----|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2**|如果`id`存在,则标识连接到此适配器的通道。
参见`id`。
可选。|
|**3**|引用`StompSessionManager` Bean,它封装了低级连接和`StompSession`处理操作。
需要。|
|**4**|引用一个实现`HeaderMapper`的 Bean,它将 Spring 集成`MessageHeaders`映射到和从
stomp 帧头映射。
它与`mapped-headers`是互斥的。
它默认为`StompHeaderMapper`。|
|**5**|用逗号分隔的要映射到 Stomp 框架标头的 Stomp 标头的名称列表。
只能提供如果`header-mapper`引用未设置。
此列表中的值也可以是简单的模式,以与标题名称进行匹配(例如`myheader*`或`*myheader`)。
一个特殊的标记(`STOMP_OUTBOUND_HEADERS`)表示所有标准的 stomp 标题(内容长度、收据、心跳),
它们是默认包含的。
如果你想添加自己的标头并希望同时映射标准标头,则必须包含该令牌或通过使用`header-mapper`提供自己的`HeaderMapper`实现。|
|**6**|STOMP 消息发送到的目的地的名称。
它与`destination-expression`是互斥的。|
|**7**|在运行时针对每个 Spring 积分`Message`作为根对象求值的 SPEL 表达式。
它与`destination`是互斥的。|
|**8**|布尔值,表示此端点是否应自动启动。
默认为`true`。|
|**9**|这个端点应该在其中开始和停止的生命周期阶段。
值越低,这个端点开始得越早,停止得越晚。
默认值是`Integer.MIN_VALUE`。
值可以是负数。
参见[`SmartLifeCycle`(https:/DOCS. Spring.io/ Spring/DOCS/current/javadoc-api/org/api/context/smartlifycle.html)。|
#### [](#understanding-the-int-stompinbound-channel-adapter-element)理解``元素
下面的清单显示了 STOMP 入站通道适配器的可用属性:
```
(11)
```
|**1** |组件 Bean name.
如果不设置`channel`属性,将在应用程序上下文中创建并注册一个`DirectChannel`,并将这个`id`属性的值作为 Bean 名称。
在这种情况下,端点以 Bean 名`id`加上`.adapter`注册。|
|------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|**2** |标识连接到此适配器的通道。|
|**3** |应该发送`ErrorMessage`实例的`MessageChannel` Bean 引用。|
|**4** |请参阅[``](#stomp-出站-通道-适配器)上的相同选项。|
|**5** |用逗号分隔的要从 stomp 帧头映射的 stomp 头的名称列表。
只有在`header-mapper`引用未设置的情况下,才能提供此选项。
此列表中的值也可以是与头名称匹配的简单模式(例如,`myheader*`或`*myheader`)。
特殊令牌(`STOMP_INBOUND_HEADERS`)表示所有标准的 Stomp 标头(内容长度、收据、心跳等)。
默认情况下,它们都包括在内。
如果你想添加自己的标头,并希望同时映射标准标头,你还必须包括这个令牌,或者使用`header-mapper`提供你自己的`HeaderMapper`实现。|
|**6** |请参阅[``](#stomp-出站-通道-适配器)上的相同选项。|
|**7** |用逗号分隔的要订阅的目标名称列表。
可以在运行时通过`addDestination()`和`removeDestination()``@ManagedOperation`注释修改目标(因此也是订阅)列表。|
|**8** |如果信道可以阻塞,则在向信道发送消息时等待的最大时间量(以毫秒为单位)。
例如,如果已达到其最大容量,则`QueueChannel`可以阻塞直到可用空间。|
|**9** |从传入的 Stomp 帧转换的目标`payload`的完全限定的 Java 类型名称。
它默认为`String.class`。|
|**10**|请参阅[``](#stomp-出站-通道-适配器)上的相同选项。|
|**11**|请参阅[``](#stomp-出站-通道-适配器)上的相同选项。|