Spring企业集成流 Spring Integration

spring集成(Spring Integration)

 在本章中,将看到如何使用 Spring Integration 的通用集成模式。Spring Integration 是由 Gregor Hohpe 和 Bobby Woolf 在*《企业级集成模式》*一书中编目的许多集成模式的实现。每个模式都被实现为一个组件,消息通过该组件传输管道中的数据。使用 Spring 配置,可以将这些组件组装到数据流经的管道中。让我们从定义一个简单的集成流开始,它引入了 Spring Integration 的许多特性。

Why??

那么,我们为什么用它呢?spring-integration的官网上,给出了以下说法

  • spring-integration的目标

    提供一个简单的模型来实现复杂的企业集成解决方案为基于spring的应用添加异步的、消息驱动的行为让更多的Spring用户来使用他看这种解释,我的直观感觉是:啥玩意?不懂啊!接着看到spring-integration的原则组件之间应该是松散的,模块性的易测的应用框架应该强迫分离业务逻辑和集成逻辑扩展节点应该有更好的抽象和可以再使用的能力

    感觉,这个应该说的是解耦吧。另外看了下其他人的理解,如果你的系统处在各个系统的中间,需要JMS交互,又需要Database/Redis/MongoDB,还需要监听Tcp/UDP等,
    还有固定的文件转移,分析。还面对着时不时的更改需求的风险。那么,它再适合不过了。

导入依赖

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-integration</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.integration</groupId>
			<artifactId>spring-integration-file</artifactId>
		</dependency>

第一项依赖是Spring Integration的spring Boot start。不管我们与哪种流进行交互,对于Spring Integration流的开发来讲,这个依赖是必须的。第二个依赖是Spring integration的文件端点模块。这个模块是与外部系统集成的二十多个模块之一。但是,就目前而言,要知道文件端点模块提供了将文件从文件系统提取到集成流或将数据从流写入文件系统的能力。

创建一个网关

//绑定要发送到的通道名称
@MessagingGateway(defaultReplyChannel = "textInChannel")
public interface FileWriterGateway {
    void writeToFile(
        //Header添加头信息 及文件名
            @Header(FileHeaders.FILENAME) String filename,
            String data
    );
    

}

尽管它是一个简单的 Java 接口,但是关于 FileWriterGateway 还有很多要说的。首先会注意到它是由 @MessagingGateway 注解的。这个注解告诉 Spring Integration 在运行时生成这个接口的实现 —— 类似于 Spring Data 如何自动生成存储库接口的实现。当需要编写文件时,代码的其他部分将使用这个接口。

@MessagingGatewaydefaultRequestChannel 属性表示,对接口方法的调用产生的任何消息都应该发送到给定的消息通道。在本例中,声明从 writeToFile() 调用产生的任何消息都应该发送到名为 textInChannel 的通道。

对于 writeToFile() 方法,它接受一个文件名作为字符串,另一个字符串包含应该写入文件的文本。关于此方法签名,值得注意的是 filename 参数使用 @Header 进行了注解。在本例中,@Header 注解指示传递给 filename 的值应该放在消息头中(指定为 FileHeaders),解析为 file_name 的文件名,而不是在消息有效负载中。另一方面,数据参数值则包含在消息有效负载中

配置集成流

xml方式配置

<?xml version="1.0" encoding="UTF-8"?>
<beans  xmlns:context="http://www.springframework.org/schema/context"
        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/spring-integration-file.xsd">
    
    <int:channel id="textInChannel"/>
    <int:transformer input-channel="textInChannel"
                     output-channel="fileWriterChannel"
                     id="upperCase"
                     expression="payload.toUpperCase()"
    />
    <int:channel id="fileWriterChannel"/>
    <int-file:outbound-channel-adapter id="writer" channel="fileWriterChannel"
                                       directory="G:\2020\temp\files\" mode="APPEND"
                                       append-new-line="true"/>
</beans>
  • 配置了一个名为 textInChannel 的通道,这与为 FileWriterGateway 设置的请求通道是相同的。当在 FileWriterGateway 上调用 writeToFile() 方法时,结果消息被发布到这个通道。
  • 配置了一个转换器来接收来自 textInChannel 的消息。它使用 Spring Expression Language(SpEL)表达式在消息有效负载上调用 toUpperCase()。然后将大写操作的结果发布到 fileWriterChannel 中。
  • 配置了一个名为 fileWriterChannel 的通道,此通道用作连接转换器和外部通道适配器的管道。
  • 最后,使用 int-file 命名空间配置了一个外部通道适配器。这个 XML 命名空间由 Spring Integration 的文件模块提供,用于编写文件。按照配置,它将接收来自 fileWriterChannel 的消息,并将消息有效负载写到一个文件中,该文件的名称在消息的 file_name 头中指定,该文件位于 directory 属性中指定的目录中。如果文件已经存在,则将用换行来追加文件,而不是覆盖它。

image-20210202222931640

如果希望在 Spring Boot 应用程序中使用 XML 配置,则需要将 XML 作为资源导入 Spring 应用程序。最简单的方法是在应用程序的 Java 配置类上使用 Spring 的 @ImportResource 注解

@Configuration
@ImportResource("classpath:/filewriter-config.xml")
public class FileWriterIntegrationConfig { ... }

编写测试类进行测试

@SpringBootTest(classes = SpringIntegrationApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class Test01 {
    @Autowired
    private FileWriterGateway fileWriterGateway;
    @Test
    public void test01(){
        fileWriterGateway.writeToFile("aaa","nihao");
    }
}
image-20210112164603094
  • 查看文件内容

image-20210112164754611

注意我们在xml配置中已经配置为将内容全部大写

java配置

@Configuration
public class FileWriterIntegrationConfig {
    @Bean
    @Transformer(inputChannel = "textInChannel",outputChannel = "fileWriterChannel")
    public GenericTransformer<String,String> upperCaseTransformer(){
        return new GenericTransformer<String, String>() {
            @Override
            public String transform(String s) {
                return s.toUpperCase();
            }
        };
    }

    @Bean
    // 声明服务激活器 处理消息
    @ServiceActivator(inputChannel = "fileWriterChannel")
    public FileWritingMessageHandler fileWriter(){
        FileWritingMessageHandler handler = new FileWritingMessageHandler(new File("G:\\2020\\temp\\files\\"));
        //设置没有答复通道 没有返回值
        handler.setExpectReply(false);
        //设置文件存在时的处理方式
        handler.setFileExistsMode(FileExistsMode.APPEND);
        //允许追加新行
        handler.setAppendNewLine(true);
        return handler;
    }
}

这里的通道如果没有spring则会自动创建

也可通过注入bean的方式进行配置

@Bean
public MessageChannel textInChannel() {
    return new DirectChannel();
}
...
@Bean
public MessageChannel fileWriterChannel() {
    return new DirectChannel();
}

可以说,Java 配置选项更易于阅读,也更简洁,而且与我在本书中所追求的纯 Java 配置完全一致。但是,通过 Spring Integration 的 Java DSL(领域特定语言)配置风格,它可以变得更加精简。

使用java DSL配置

 @Bean
    public IntegrationFlow fileWriterFlow(){
        //入站通道
        return IntegrationFlows
                .from(MessageChannels.direct("textInChannel"))
                .channel(MessageChannels.direct("fileWriterChannel"))
                //声明转换器
                .<String,String>transform(t->t.toUpperCase())
                .handle(Files.outboundAdapter(new File("G:\\2020\\temp\\files\\"))
                        .fileExistsMode(FileExistsMode.APPEND)
                        .appendNewLine(true)
                ).get();
    }

这个新配置尽可能简洁,用一个 bean 方法捕获整个流。IntegrationFlows 类初始化了这个构建者 API,可以从该 API 声明流。

注意,与 Java 配置示例一样,不需要显式地声明通道 bean。虽然引用了 textInChannel,但它是由 Spring Integration 自动创建的,因为没有使用该名称的现有通道 bean。但是如果需要,可以显式地声明通道 bean。

Spring Integration 功能概览

  • 通道 Channels —— 将信息从一个元素传递到另一个元素。
  • 过滤器 Filters —— 有条件地允许基于某些标准的消息通过流。
  • 转换器 Transformers —— 更改消息值或将消息有效负载从一种类型转换为另一种类型。
  • 路由器 Routers —— 直接将信息发送到几个渠道之一,通常是基于消息头。
  • 切分器Splitters —— 将收到的信息分成两条或多条,每条都发送到不同的渠道。
  • 聚合器 Aggregators —— 与分离器相反,它将来自不同渠道的多条信息组合成一条信息。
  • 服务激活器 Service activators —— 将消息传递给某个 Java 方法进行处理,然后在输出通道上发布返回值。
  • 通道适配器 Channel adapters —— 将通道连接到某些外部系统或传输。可以接受输入,也可以向外部系统写入。
  • 网关 Gateways —— 通过接口将数据传递到集成流。

消息通道

​ 消息通道意指消息移动的集成管道移动。它们是连接 Spring Integration 所有其他部分的管道。

Spring Integration 提供了多个管道的实现,包括以下这些:

  • 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)

在 Java 配置和 Java DSL 样式中,输入通道都是自动创建的,默认是 DirectChannel。但是,如果希望使用不同的通道实现,则需要显式地将通道声明为 bean 并在集成流中引用它。例如,要声明 PublishSubscribeChannel,需要声明以下 @Bean 方法:

 @Bean
    public MessageChannel orderChannel(){
        return new PublishSubscribeChannel();
    }

然后在集成流定义中通过名称引用这个通道。例如,如果一个服务 activator bean 正在使用这个通道,那么可以在 @ServiceActivatorinputChannel 属性中引用它:

@ServiceActovator(inputChannel="orderChannel")

或者,如果使用 Java DSL 配置方式,需要通过调用 channel() 方法引用它:

@Bean
public IntegrationFlow orderFlow() {
    return IntegrationFlows
        ...
        .channel("orderChannel")
        ...
        .get();
}

需要注意的是,如果使用 QueueChannel,则必须为使用者配置一个轮询器。例如,假设已经声明了一个这样的 QueueChannel bean:

@Bean
public MessageChannel orderChannel() {
    return new QueueChannel();
}

需要确保将使用者配置为轮询消息通道。在服务激活器的情况下,@ServiceActivator 注解可能是这样的:

@ServiceActivator(inputChannel="orderChannel", poller=@Poller(fixedRate="1000"))

在本例中,服务激活器每秒(或 1,000 ms)从名为 orderChannel 的通道轮询一次。

过滤器

过滤器置于集成管道之间,它能够根据断言允许或拒绝消息进入流程的下一步.

​ 例如,假设消息包含整型的值,它们要通过名为numberChannel的通道进行发布,但是我们只想让偶数进入名为evenNumberChannel的通道。在这种情况下,我们可以使用@Filter注解定义一个过滤器:

 @Filter(inputChannel = "numberChannel",outputChannel = "evenNumberChannel")
    public boolean evenNumberFilter(Integer integer){
        return integer%2==0;
    }

过滤器可以放置在集成管道的中间,以允许或不允许消息进入流中的下一个步骤。

例如,假设包含整数值的消息通过名为 numberChannel 的通道发布,但是只希望偶数传递到名为 evenNumberChannel 的通道。在这种情况下,可以使用 @Filter 注解声明一个过滤器,如下所示:

@Bean
public IntegrationFlow evenNumberFlow(AtomicInteger integerSource) {
    return IntegrationFlows
        ...
        .<Integer>filter((p) -> p % 2 == 0)
        ...
        .get();
}

转换器

​ 转换器会对消息执行一些操作,一般会形成不同的消息,有可能会产生不同的载荷类型

  • 这里将数字转换为罗马数
 @Bean
    @Transformer(inputChannel = "textInChannel",outputChannel = "fileWriterChannel")
    public GenericTransformer<Integer,String> upperCaseTransformer(){
        return RomanNumbers::toRoman;
    }

也可用java DSL配置

 @Bean
    public IntegrationFlow transformerFlow(){
        return IntegrationFlows
                .from(MessageChannels.direct("textInChannel"))
                .channel(MessageChannels.direct("fileWriterChannel"))
                //声明转换器
                .<Integer,String>transform(RomanNumbers::toRoman)
                .handle(Files.outboundAdapter(new File("G:\\2020\\temp\\files\\"))
                        .fileExistsMode(FileExistsMode.APPEND)
                        .appendNewLine(true)
                ).get();
    }

路由器

假设我们有一个名为numberChannel的通道,他会传输整型值。我们想要有偶数的消息定位到名为evenChannel通道,奇数消息定位到oddChannel通道。要在集成流中创建这样一个路由器,我们可以声明一个AbstractMessageRouter类型的bean,并为其添加@Router注解:

 @Bean
    @Router(inputChannel = "numberChannel")
    public AbstractMessageRouter evenOddRouter(){
        return new AbstractMessageRouter() {
            @Override
            protected Collection<MessageChannel> determineTargetChannels(Message<?> message) {
                Integer payload = (Integer) message.getPayload();
                if (payload%2==0){
                    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(RomanNumbers::toRoman).handle((i,h) -> { ... }))
            )
        .get();
}

虽然仍然可以声明 AbstractMessageRouter 并将其传递给 route(),但是本例使用 lambda 表达式来确定消息有效负载是奇数还是偶数。

如果是偶数,则返回一个偶数的字符串值。如果是奇数,则返回奇数。然后使用这些值来确定哪个子映射将处理消息。

分割器

有时,在集成流中,将消息拆分为多个独立处理的消息可能很有用。Splitter 将为分割并处理这些消息。

Splitter 在很多情况下都很有用,但是有两个基本用例可以使用 Splitter:

  • 消息有效载荷,包含单个消息有效载荷相同类型的项的集合。例如,携带产品列表的消息可能被分成多个消息,每个消息的有效负载是一个产品。
  • 信息有效载荷,携带的信息虽然相关,但可以分为两种或两种以上不同类型的信息。例如,购买订单可能包含交付、帐单和行项目信息。交付细节可能由一个子流程处理,账单由另一个子流程处理,每一项则由另一个子流程处理。在这个用例中,Splitter 后面通常跟着一个路由器,它根据有效负载类型路由消息,以确保正确的子流处理数据。
image-20210114162359243

当将消息有效负载拆分为两个或多个不同类型的消息时,通常只需定义一个 POJO 即可,该 POJO 提取传入的有效负载的各个部分,并将它们作为集合的元素返回。

例如,假设希望将携带购买订单的消息拆分为两条消息:一条携带账单信息,另一条携带项目列表。下面的 OrderSplitter 将完成这项工作:

public class OrderSplitter {
    public Collection<Object> splitOrderIntoParts(PurchaseOrder po){
        ArrayList<Object> list = new ArrayList<>();
        list.add(po.getBillingInfo());
        //lineItems是一个list集合
        list.add(po.getLineItems());
        return list;
    }
}

接下来,我们生明一个OrderSplitter bean,并通过@Splitter注解将其作为集成流的一部分

@Bean
    @Splitter(inputChannel = "poChannel",outputChannel = "splitOrderChannel")
    public OrderSplitter orderSplitter(){
        return new OrderSplitter();
    }

在这里声明订单会到达名为poChannel的通道,他们会将OrderSplitter切分。然后,返回集合中的每个条目将会作为集成流中独立的消息,他们会发布到名为splitOrderChannel的通道上,声明一个路由器分别路由到他们自己的子流上:

 @Bean
    @Router(inputChannel = "splitOrderChannel")
    public MessageRouter splitOrderRouter(){
        PayloadTypeRouter payloadTypeRouter = new PayloadTypeRouter();
        payloadTypeRouter.setChannelMapping(BillingInfo.class.getName(),"billingInfoChannel");
        payloadTypeRouter.setChannelMapping(List.class.getName(),"lineItemsChannel");
        return payloadTypeRouter;
    }

可以再对lineItems进行分割

@Bean
    @Splitter(inputChannel = "lineItemsChannel",outputChannel = "lineItemChannel")
    public OrderSplitter orderSplitter(){
        return new OrderSplitter();
    }

这样lineItems集合中的每个元素都会发布到一个消息中,这些消息将会被发布到名为lineItemChannel的通道中。

使用java DSL方式实现同样的splitter/router配置,那么我们可以通过调用split()和router()来实现

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();

服务激活器

服务激活器接受来自输入通道的消息并将这些消息发送到一个MessageHandler的实现。

image-20210115193910725

Spring integration 提供了多个开箱即用的MessageHandler (payloadTypeRouter甚至就是messageHandler的实现),但是我们通常会需要提供一些自定义的实现作为服务激活器。作为样例,如下代码声明了如何声明MessageHandler bean并将其配置为服务激活器:

@Bean
    @ServiceActivator(inputChannel = "someChannel")
    public MessageHandler sysoutHandler(){
        return message -> System.out.println("Message payload:"+message.getPayload());

    }

​ 这个bean使用了@ServiceActivator注解,表明它会作为一个服务激活器处理来自someChannel通道的消息。

 @Bean
    @ServiceActivator(inputChannel = "someChannel")
    public GenericHandler<Order> sysoutHandler(){
        return ((payload, headers) -> orderRepo.save(payload));

    }

在本例中服务器会接受一个载荷类型为Order的消息。当订单抵达时我们会通过一个repository将它保存起来,并返回保存一个order,这个order随后被发送至名为completeChannel的输出通道。

GenericHandler不仅可以获取载荷,并且可以获取消息头

java DSL配置

public IntegrationFlow someFlow() {
    return IntegrationFlows
        ...
        .handle(msg -> {
            System.out.println("Message payload: " + msg.getPayload());
        })
        .get();
}
  • GenericHandler<Order>用java DSL配置
public IntegrationFlow orderFlow(OrderRepository orderRepo) {
    return IntegrationFlows
        ...
        .<Order>handle((payload, headers) -> {
            return orderRepo.save(payload);
        })
        ...
        .get();
}

当利用 GenericHandler 时,lambda 表达式或方法参考接受该消息的有效载荷和报头作为参数。另外,如果选择在一个流程的结束使用 GenericHandler,需要返回 null,否则会得到这表明有没有指定输出通道的错误。

网关

通过网关,应用可以提交数据到集成流中,并且能够可选地接收流的结果作为响应。网关会声明为接口。

image-20210115202519169
//FileWriterGateway就是一个网关
@Component
@MessagingGateway(defaultRequestChannel = "textInChannel",defaultReplyChannel="outChannel")
public interface FileWriterGateway {
    void writeToFile(
            @Header(FileHeaders.FILENAME) String filename,
            String data
    );

}

这个接口不需要实现Spring integration会自动在运行时提供一个实现,它会通过特定的通道发送和接受消息。

当接口中的方法被调用时,给定的string流会发送到集成流中,进入名为“inChannel”的通道。不管流是如何定义的或者它都干了些什么,当数据进入名为outChannel的通道时,他将会从方法返回。

return IntegrationFlows
                .from("inChannel")
                .<String,String>transform(s->s.toUpperCase())
                .channel("outChannel")
                .get();

​ 按照定义,这个流会从进入InChannel通道的数据开始,消息载荷会由转换器进行处理,也就是执行大写操作(通过lambda表达式进行)。结果形成的消息被发送到名为outChannel通道,也就是我们在UpperCaseGateWay中声明的答复通道。

通道适配器

​ 通道适配器代表了集成流入口和出口。数据通过入站通道适配器(inbound channel adapter)进入一个集成流,通过出站通道适配器离开一个集成流,

image-20210117144715443

通道适配器代表集成信息流的入口点和出口点。数据通过入站信道适配器的方式进入到集成流中,通过出站信道适配器的方式离开集成流。

入站信道的适配器可以采取多种形式,这取决于它们引入到流的数据源。例如,声明一个入站通道适配器,它采用从 AtomicInteger 到流递增的数字。使用 Java 配置,它可能是这样的:

@Bean
@InboundChannelAdapter(
    poller=@Poller(fixedRate="10"), channel="numberChannel")
public MessageSource<Integer> numberSource(AtomicInteger source) {
    return () -> {
        return new GenericMessage<>(source.getAndIncrement());
    };
}

测试这个适配器

要测试这个通道适配器还必须要声明一个服务激活器 也就是出站通道适配器

@Bean
    @ServiceActivator(inputChannel = "numberChannel")
    public MessageHandler header(){
        return System.out::println;
    }

让他直接打印结果

  • 声明队列
 @Bean
    public MessageChannel numberChannel() {
        return new QueueChannel();
    }

注意:还必须声明一个全局的轮询器

@Bean(name = PollerMetadata.DEFAULT_POLLER)
    public PollerMetadata defaultPoller() {

        PollerMetadata pollerMetadata = new PollerMetadata();
        pollerMetadata.setTrigger(new PeriodicTrigger(10));
        return pollerMetadata;
    }

也可已通过ServiceActivator注解配置poller属性

xml中如下配置

<int:poller default="true" fixed-delay="50"/>

不写的话会报错 https://stackoverflow.com/questions/20799737/spring-integration-no-poller-has-been-defined-for-endpoint

java.lang.IllegalArgumentException: No poller has been defined for endpoint 'org.springframework.integration.config.ConsumerEndpointFactoryBean#2', and no default poller is available within the context.

编写测试类

@Autowired
    private FileWriterGateway fileWriterGateway;
    @Autowired
    private AtomicInteger atomicInteger;
    @Test
    public void test01(){
        //调用网关接口
        fileWriterGateway.writeToFile(atomicInteger);
    }
  • 结果
image-20210117165009991

当使用 Java 配置时,@InboundChannelAdapter 意味着是一个入站通道适配器,from() 方法就是使用 Java DSL 来定义流的时候,表明它是怎么处理的。下面对于流定义的一个片段展示了在 Java DSL 配置中类似的输入通道适配器:

@Bean
public IntegrationFlow someFlow(AtomicInteger integerSource) {
    return IntegrationFlows
        .from(integerSource, "getAndIncrement",
              c -> c.poller(Pollers.fixedRate(1000)))
        ...
        .get();
}

通常情况下,通道适配器通过的 Spring Integration 的多端点模块之一进行提供。举个例子,假设需要一个入站通道适配器,用它来监视指定的目录,同时将任何写入到那个目录中的文件作为消息,提交到名为 file-channel 的通道中。下面的 Java 配置使用 FileReadingMessageSource 从 Spring Integration 的文件端点模块来实现这一目标:

@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;
}

当在 Java DSL 中写入同样的 file-reading 入站通道适配器时,来自 Files 类的 inboundAdapter() 方法达到的同样的目的。出站通道适配器位于集成信息流的最后位置,将最终消息扇出到应用程序或是其他系统中:

@Bean
public IntegrationFlow fileReaderFlow() {
    return IntegrationFlows
        .from(Files.inboundAdapter(new File(INPUT_DIR))
              .patternFilter(FILE_PATTERN))
        .get();
}

服务激活器(作为消息处理的实现)往往是为出站通道适配器而存在的,特别是当数据需要被扇出到应用程序本身的时候。

读取信息实战

https://blog.csdn.net/qq_40929047/article/details/89569887

posted @ 2021-02-02 22:36  錵開や落幕  阅读(4844)  评论(2编辑  收藏  举报