新增IntegrationFlow后功能测试中SourcePollingChannelAdapter创建报错
针对你的问题,有几个实用的替代方案可以在功能测试中启停SFTP轮询,避免Bean冲突和循环引用问题:
1. 给每个SourcePollingChannelAdapter显式设置ID,通过@Qualifier注入
在定义IntegrationFlow时,直接给源头的SourcePollingChannelAdapter设置唯一ID,而非给轮询器设置ID:
@Bean public IntegrationFlow readingFilesAndPrintContent() { return IntegrationFlows.from(sftpInboundAdapter(), e -> e.id("printingFileAdapter") // 给SourcePollingChannelAdapter设置唯一ID .poller(p -> p.fixedDelay(10000).maxMessagesPerPoll(1))) .handle(System.out::println) .get(); } @Bean public IntegrationFlow readingFilesAndRemoving() { return IntegrationFlows.from(anotherSftpInboundAdapter(), e -> e.id("removingFileAdapter") // 另一个适配器的唯一ID .poller(p -> p.fixedDelay(5000).maxMessagesPerPoll(1))) .handle((payload, headers) -> { // 执行文件删除逻辑 return null; }) .get(); }
然后在测试中通过@Qualifier注入指定适配器:
@Autowired @Qualifier("printingFileAdapter") private SourcePollingChannelAdapter printingAdapter; @Autowired @Qualifier("removingFileAdapter") private SourcePollingChannelAdapter removingAdapter; // 测试启停逻辑 @Test void testPollingControl() { printingAdapter.start(); // 执行文件读取打印的测试逻辑 printingAdapter.stop(); removingAdapter.start(); // 执行文件删除的测试逻辑 removingAdapter.stop(); }
这种方式能避免之前的循环引用问题,因为你直接注入的是适配器本身,而非依赖的轮询器组件。
2. 使用Spring Integration Control Bus控制启停
通过Control Bus发送消息来控制适配器状态,无需直接注入SourcePollingChannelAdapter:
首先配置Control Bus:
@Bean public IntegrationFlow controlBusFlow() { return IntegrationFlows.from("controlBusInput") .controlBus() .get(); }
然后在测试中注入消息通道,发送控制指令:
@Autowired @Qualifier("controlBusInput") private MessageChannel controlBusChannel; // 启动打印适配器 controlBusChannel.send(MessageBuilder.withPayload("@printingFileAdapter.start()").build()); // 停止打印适配器 controlBusChannel.send(MessageBuilder.withPayload("@printingFileAdapter.stop()").build()); // 启动删除适配器 controlBusChannel.send(MessageBuilder.withPayload("@removingFileAdapter.start()").build()); // 停止删除适配器 controlBusChannel.send(MessageBuilder.withPayload("@removingFileAdapter.stop()").build());
Control Bus支持通过SpEL表达式调用Spring Bean的方法,这种方式完全避免了直接注入适配器的问题,适合多组件的场景。
3. 通过IntegrationFlowContext获取适配器
如果不想给适配器设置ID,可以通过IntegrationFlowContext获取对应的IntegrationFlow,再从中提取适配器:
@Autowired private IntegrationFlowContext flowContext; @Test void testPollingViaFlowContext() { // 获取打印文件的IntegrationFlow IntegrationFlow printFlow = flowContext.getFlow("readingFilesAndPrintContent"); // 从Flow中获取SourcePollingChannelAdapter SourcePollingChannelAdapter printAdapter = (SourcePollingChannelAdapter) printFlow.getComponent("printingFileAdapter"); printAdapter.start(); // 同理操作删除文件的Flow IntegrationFlow removeFlow = flowContext.getFlow("readingFilesAndRemoving"); SourcePollingChannelAdapter removeAdapter = (SourcePollingChannelAdapter) removeFlow.getComponent("removingFileAdapter"); removeAdapter.start(); }
注意:如果没有给适配器设置ID,Spring会生成默认组件名,你可以通过调试查看具体名称;优先使用方案1显式设置ID,代码更易维护。
为何之前的@Qualifier会出现循环引用?
你之前给轮询器设置ID并注入,导致Spring在创建Bean时出现循环依赖:SourcePollingChannelAdapter依赖PollerMetadata,而你又尝试通过PollerMetadata的ID注入适配器,触发了Bean创建的循环逻辑。直接给SourcePollingChannelAdapter设置ID就能避免这个问题。
内容的提问来源于stack exchange,提问作者Vasilis Iak

