一、集成流integration。
pom.xml引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-integration-file</artifactId>
</dependency>
将方法转换为消息:
// @MessagingGateway 的 defaultRequestChannel 属性表示,
// 对接口方法的调用产生的任何消息都应该发送到给定的消息通道。
@MessagingGateway(defaultRequestChannel="textInChannel")
public interface FileWriterGateway {
void writeToFile(
// @Header 注解指示传递给 filename 的值应该放在消息头中
@Header(FileHeaders.FILENAME) String filename,
String data);
}
还需要配置集成流。
方式1、XML 配置集成流
filewriter-config.xml
:
<?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-file="http://www.springframework.org/schema/integration/file"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/integration/file
http://www.springframework.org/schema/integration/file/
springintegration-file.xsd">
<!-- ①
配置了一个名为 textInChannel 的通道,这与上面java代码中的 FileWriterGateway 设置的请求通道是相同的。
当在 FileWriterGateway 上调用 writeToFile() 方法时,结果消息被发布到这个通道-->
<int:channel id="textInChannel" />
<!-- ② 转换器。
expression="payload.toUpperCase()" : 通过SpEL表示式中toUpperCase();
output-channel="fileWriterChannel" :将大写操作的结果发布到 fileWriterChannel 中;
input-channel="textInChannel":配置了一个转换器来接收来自 textInChannel 的消息。
-->
<int:transformer id="upperCase" input-channel="textInChannel"
output-channel="fileWriterChannel" expression="payload.toUpperCase()" />
<!-- ③ 该通道用作连接。
配置了一个名为 fileWriterChannel 的通道,
该通道用作连接 转换器 和 外部通道适配器的 管道。-->
<int:channel id="fileWriterChannel" />
<!-- ④ int-file:outbound-channel-adapter: 命名空间配置了一个外部通道适配器。
channel="fileWriterChannel":接收来自 fileWriterChannel 的消息;
并写到一个文件中,文件的名称在消息的@Header头指定(在上面java代码中指定,这里就是filename);
directory:指定文件存放目录。
mode="APPEND":指追加;
append-new-line="true": 换行
-->
<int-file:outbound-channel-adapter id="writer" channel="fileWriterChannel"
directory="/tmp/springbootlearndemo/files" mode="APPEND" append-new-line="true" />
</beans>
@Configuration
@ImportResource("classpath:/filewriter-config.xml")
public class FileWriterIntegrationConfig {
//...
}
方式2、Java 配置集成流
@Configuration
public class FileWriterIntegrationConfig {
// 转换器: GenericTransformer。
// 转换器的 bean使用 @Transformer 进行注解
@Bean
@Transformer(inputChannel="textInChannel", outputChannel="fileWriterChannel")
public GenericTransformer<String, String> upperCaseTransformer() {
return text -> text.toUpperCase();
}
// 用 @ServiceActivator 注解 来表示将接受来自 fileWriterChannel 的消息,
// 并将这些消息传递给由 FileWritingMessageHandler 实例定义的服务。
@Bean
@ServiceActivator(inputChannel="fileWriterChannel")
public FileWritingMessageHandler fileWriter() {
FileWritingMessageHandler handler =
new FileWritingMessageHandler(new File("/tmp/springbootlearndemo/files"));
handler.setExpectReply(false);
handler.setFileExistsMode(FileExistsMode.APPEND);
handler.setAppendNewLine(true);
return handler;
}
}
方式3、使用 DSL 进行 Java 配置
@Configuration
public class FileWriterIntegrationConfig {
@Bean
public IntegrationFlow fileWriterFlow() {
return IntegrationFlows
.from(MessageChannels.direct("textInChannel"))
.<String, String>transform(t -> t.toUpperCase())
.handle(Files.outboundAdapter(new File("/tmp/springbootlearndemo/files"))
.fileExistsMode(FileExistsMode.APPEND)
.appendNewLine(true))
.get();
}
}
二、 Spring Integration组件
组件 | 描述 |
---|---|
Channels | 将信息从一个元素传递到另一个元素。 |
Filters | 有条件地允许基于某些标准的消息通过流。 |
Transformers | 更改消息值或将消息有效负载从一种类型转换为另一种类型。 |
Routers | 直接将信息发送到几个渠道之一,通常是基于消息头。 |
Splitters | 将收到的信息分成两条或多条,每条都发送到不同的渠道。 |
Aggregators | 与分离器相反,它将来自不同渠道的多条信息组合成一条信息。 |
Service activators | 将消息传递给某个 Java 方法进行处理,然后在输出通道上发布返回值。 |
Channel adapters | 将通道连接到某些外部系统或传输。可以接受输入,也可以向外部系统写入。 |
Gateways | 通过接口将数据传递到集成流 |
1、消息通道
管道实现 | 描述 |
---|---|
PublishSubscribeChannel | 消息被发布到 PublishSubscribeChannel 后又被传递给一个或多个消费者。如果有多个消费者,他们都将会收到消息。 |
QueueChannel | 消息被发布到 QueueChannel 后被存储到一个队列中,直到消息被消费者以先进先出(FIFO)的方式拉取。如果有多个消费者,他们中只有一个能收到消息。 |
PriorityChannel | 与 QueueChannel 类似,但是与 FIFO 方式不同,消息被冠以 priority 的消费者拉取。 |
RendezvousChannel | 与 QueueChannel 期望发送者阻塞通道,直到消费者接收这个消息类似,这种方式有效的同步了发送者与消费者。 |
DirectChannel | 与 PublishSubscribeChannel 类似,但是是通过在与发送方相同的线程中调用消费者来将消息发送给单个消费者,此通道类型允许事务跨越通道。 |
ExecutorChannel | 与 DirectChannel 类似,但是消息分派是通过 TaskExecutor 进行的,在与发送方不同的线程中进行,此通道类型不支持事务跨通道。 |
FluxMessageChannel | Reactive Streams Publisher 基于 Project Reactor Flux 的消息通道。(我们将会在第 10 章讨论 Reactive Streams、Reactor 和 Flux) |
①使用PublishSubscribeChannel实现:
1)定一个bean
@Bean
public MessageChannel orderChannel() {
return new PublishSubscribeChannel();
}
2)在集成流定义中通过名称引用这个通道:
@ServiceActovator(inputChannel="orderChannel")
或者Java DSL配置:
@Bean
public IntegrationFlow orderFlow() {
return IntegrationFlows
// ...
.channel("orderChannel")
// ...
.get();
}
②使用QueueChannel实现
1)定义一个bean:
@Bean
public MessageChannel orderChannel() {
return new QueueChannel();
}
2)@ServiceActivator 注解:
@ServiceActivator(inputChannel="orderChannel", poller=@Poller(fixedRate="2000"))
2、过滤器
将奇数的消息传递到通道
@Bean
public IntegrationFlow numberFlow(AtomicInteger integerSource) {
return IntegrationFlows
...
.<Integer>filter((p) -> p % 2 == 1)
...
.get();
}
3、转换器
添加 @Transformer
注解:
@Bean
@Transformer(inputChannel="numberChannel", outputChannel="zhcnNumberChannel")
public GenericTransformer<Integer, String> zhcnNumTransformer() {
return ZhcnNumbers::toZhcn;
}
用Java DSL 配置风格,如下:
@Bean
public IntegrationFlow transformerFlow() {
return IntegrationFlows
// ...
.transform(ZhcnNumbers::toZhcn)
// ...
.get();
}
4、路由
@Bean
// 接受来自名为 numberChannel 的输入通道的消息
@Router(inputChannel="numberChannel")
public AbstractMessageRouter evenOddRouter() {
return new AbstractMessageRouter() {
@Override
protected Collection<MessageChannel>
determineTargetChannels(Message<?> message) {
Integer number = (Integer) message.getPayload();
if (number % 2 == 0) {
// 返回名为 evenChannel 的通道
return Collections.singleton(evenChannel());
}
return Collections.singleton(oddChannel());
}
};
}
@Bean
public MessageChannel evenChannel() {
return new DirectChannel();
}
@Bean
public MessageChannel oddChannel() {
return new DirectChannel();
}
Java DSL 形式:
@Bean
public IntegrationFlow numberRoutingFlow(AtomicInteger source) {
return IntegrationFlows
//
.<Integer, String>route(n -> n%2==0 ? "EVEN":"ODD", mapping ->
mapping.subFlowMapping("EVEN", sf ->
sf.<Integer, Integer>transform(n -> n * 10).handle((i,h) -> { //
}))
.subFlowMapping("ODD", sf ->
sf.transform(ZhcnNumbers::toZhcn).handle((i,h) -> {
//
}))
)
.get();
}
5、分割器
消息有效载荷 | 包含单个消息有效载荷相同类型的项的集合。 |
信息有效载荷 | 携带的信息虽然相关,但可以分为两种或两种以上不同类型的信息。 |
public class OrderSplitter {
public Collection<Object> splitOrderIntoParts(PurchaseOrder po) {
ArrayList<Object> parts = new ArrayList<>();
parts.add(po.getBillingInfo());
parts.add(po.getLineItems());
return parts;
}
}
使用 @Splitter
注解将 OrderSplitter bean 声明为集成流的一部分:
// 购买订单到达名为 poChannel 的通道,并被 OrderSplitter 分割。
// 将返回集合中的每个项作为集成流中的单独消息发布到名为 splitOrderChannel 的通道。
@Bean
@Splitter(inputChannel="poChannel", outputChannel="splitOrderChannel")
public OrderSplitter orderSplitter() {
return new OrderSplitter();
}
声明一个 PayloadTypeRouter 来将账单信息和项目,并路由到它们自己的子流:
一个是 BillingInfo 对象流,另一个是 List 流
@Bean
@Router(inputChannel="splitOrderChannel")
public MessageRouter splitOrderRouter() {
PayloadTypeRouter router = new PayloadTypeRouter();
// 有效负载为类型为 BillingInfo 的消息路由到一个名为 billingInfoChannel 的通道
router.setChannelMapping(
BillingInfo.class.getName(), "billingInfoChannel");
// 项目信息,在 java.util.List 集合包
router.setChannelMapping(List.class.getName(), "lineItemsChannel");
return router;
}
处理每个 LineItem,将列表拆分为多个消息(每个行项对应一条消息):
@Splitter(inputChannel="lineItemsChannel", outputChannel="lineItemChannel")
public List<LineItem> lineItemSplitter(List<LineItem> lineItems) {
return lineItems;
}
使用 Java DSL 来声明:
return IntegrationFlows
// ...
.split(orderSplitter())
.<Object, String> route(p -> {
if (p.getClass().isAssignableFrom(BillingInfo.class)) {
return "BILLING_INFO";
} else {
return "LINE_ITEMS";
}
}, mapping ->
mapping.subFlowMapping("BILLING_INFO", sf ->
sf.<BillingInfo> handle((billingInfo, h) -> {
//...
}))
.subFlowMapping("LINE_ITEMS", sf ->
sf.split().<LineItem> handle((lineItem, h) -> {
// ...
}))
)
.get();
6、 服务激活器
// 通过 @ServiceActivator 注解 bean,将其指定为一个服务激活器,处理消息helloChannel。
@Bean
@ServiceActivator(inputChannel="helloChannel")
public MessageHandler systemoutHandler() {
return message -> {
System.out.println(message.getPayload());
};
}
// 返回一个新的有效载荷之前处理传入的消息
@Bean
@ServiceActivator(inputChannel="orderChannel", outputChannel="completeOrder")
public GenericHandler<Order> orderHandler(OrderRepository orderRepo) {
// 保存 Order 后产生的结果被发送到名称为 completeChannel 的输出通道
return (payload, headers) -> {
return orderRepo.save(payload);
};
}
Java DSL 配置
public IntegrationFlow someFlow() {
return IntegrationFlows
// ...
.handle(msg -> {
System.out.println(msg.getPayload());
})
.get();
}
7、网关
网关是实现为应用程序可以调用将消息发送到集成信息流的接口。
package com.example.demo;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.stereotype.Component;
// 给定的 String 被发布到名为 inChannel 的集成流通道中。
// 不管流是如何定义的或是它是做什么的,在当数据到达名为 outChannel 通道时,
// 它从 uppercase() 方法中返回。
@Component
@MessagingGateway(defaultRequestChannel="inChannel", defaultReplyChannel="outChannel")
public interface UpperCaseGateway {
String uppercase(String in);
}
Java DSL 配置:
@Bean
public IntegrationFlow uppercaseFlow() {
return IntegrationFlows
.from("inChannel")
.<String, String> transform(s -> s.toUpperCase())
.channel("outChannel")
.get();
}
8、通道适配器
集成信息流的入口点和出口点。
@Bean
// @InboundChannelAdapter 注解,它们每 1 秒(1000 ms)从注入的
// AtomicInteger 提交一个数字到名 numberChannel 的通道中
@InboundChannelAdapter(
poller=@Poller(fixedRate="1000"), channel="numberChannel")
public MessageSource<Integer> numberSource(AtomicInteger source) {
return () -> {
return new GenericMessage<>(source.getAndIncrement());
};
}
Java DSL 配置中类似的输入通道适配器:
@Bean
public IntegrationFlow someFlow(AtomicInteger integerSource) {
return IntegrationFlows
.from(integerSource, "getAndIncrement",
c -> c.poller(Pollers.fixedRate(1000)))
// ...
.get();
}
@Bean
@InboundChannelAdapter(channel="file-channel",
poller=@Poller(fixedDelay="1000"))
public MessageSource<File> fileReadingMessageSource() {
FileReadingMessageSource sourceReader = new FileReadingMessageSource();
sourceReader.setDirectory(new File(INPUT_DIR));
sourceReader.setFilter(new SimplePatternFileListFilter(FILE_PATTERN));
return sourceReader;
}
@Bean
public IntegrationFlow fileReaderFlow() {
// Java DSL 中写入同样的 file-reading 入站通道适配器时,使用Files 类的 inboundAdapter() 方法。
// 出站通道适配器位于集成信息流的最后位置,将最终消息发到应用程序或是其他系统中。
return IntegrationFlows
.from(Files.inboundAdapter(new File(INPUT_DIR))
.patternFilter(FILE_PATTERN))
.get();
}
9、端点模块
模块 | 依赖的 Artifact ID |
---|---|
AMQP | spring-integration-amqp |
Spring application events | spring-integration-event |
RSS and Atom | spring-integration-feed |
Filesystem | spring-integration-file |
FTP/FTPS | spring-integration-ftp |
GemFire | spring-integration-gemfire |
HTTP | spring-integration-http |
JDBC | spring-integration-jdbc |
JPA | spring-integration-jpa |
JMS | spring-integration-jms |
spring-integration-mail | |
MongoDB | spring-integration-mongodb |
MQTT | spring-integration-mqtt |
Redis | spring-integration-redis |
RMI | spring-integration-rmi |
SFTP | spring-integration-sftp |
STOMP | spring-integration-stomp |
Stream | spring-integration-stream |
Syslog | spring-integration-syslog |
TCP/UDP | spring-integration-ip |
spring-integration-twitter | |
Web | Services spring-integration-ws |
WebFlux | spring-integration-webflux |
WebSocket | spring-integration-websocket |
XMPP | spring-integration-xmpp |
ZooKeeper | spring-integration-zookeeper |