You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

新增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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 11:11:29