Spring 集成:重用 MessageProducer 定义

Spring Integration: reuse MessageProducer definition

我有一个经过精心设置的用于 soap 调用的出站网关 (MarshallingWebServiceOutboundGateway)。我需要使用来自多个流的网关定义。 问题 有点相似,但这个问题是关于 spring bean 范围原型的正确使用 spring 集成协作者。

我有一个单独的配置文件来设置网关及其依赖项:

@Bean
public MarshallingWebServiceOutboundGateway myServiceGateway() {
    Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
    marshaller.setPackagesToScan("blah.*");

    MarshallingWebServiceOutboundGateway gateway = new MarshallingWebServiceOutboundGateway(
            serviceEndpoint, marshaller, messageFactory);
    gateway.setMessageSender(messageSender);
    gateway.setRequestCallback(messageCallback);

    return gateway;
}

这就是我最初尝试从两个不同配置文件中的两个不同流连接出站网关的方式。

在一个配置文件中:

@Bean
public IntegrationFlow flow1() {
    MarshallingWebServiceOutboundGateway myServiceGateway = context.getBean("myServiceGateway", MarshallingWebServiceOutboundGateway.class);

    return IntegrationFlows
            .from(Http.inboundGateway("/res1")
                    .requestMapping(r -> r.methods(HttpMethod.GET))
            .transform(soapRequestTransformer)
            .handle(myServiceGateway) // wrong: cannot be same bean
            .transform(widgetTransformer)
            .get();
}

在单独的配置文件中:

@Bean
public IntegrationFlow flow2() {
    MarshallingWebServiceOutboundGateway myServiceGateway = context.getBean("myServiceGateway", MarshallingWebServiceOutboundGateway.class);

    return IntegrationFlows
            .from(Http.inboundGateway("/res2")
                    .requestMapping(r -> r.methods(HttpMethod.GET))
            .transform(soapRequestTransformer)
            .handle(myServiceGateway) // wrong: cannot be same bean
            .transform(widgetTransformer)
            .handle(servicePojo)
            .get();
}

这是一个问题,因为 - 据我了解 - myServiceGateway 不能是同一个实例,因为该实例只有一个出站通道并且不能属于两个不同的流。

在相关问题 中,@artem-bilan 建议不要在 @Bean 方法中创建出站网关,而是使用为每次调用创建新实例的普通方法。

可行,但对我来说不方便。我需要在不同的配置文件中重用多个流的出站网关,并且我必须复制代码以在每个配置文件中创建网关。此外,网关依赖项使我的配置文件构造函数膨胀,使 Sonar 保释。

由于 IntegrationFlowDefinition.checkReuse() 的错误消息显示 A reply MessageProducer may only be referenced once (myServiceGateway) - use @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) on @Bean definition. 我想再试一次示波器原型。

所以我尝试让 spring 集成通过名称从上下文中查找原型网关,希望在 flow1 和 flow2 中获得不同的网关实例:

.handle(context.getBean("myServiceGateway", 
    MarshallingWebServiceOutboundGateway.class))

并且我用

注释了出站网关 @Bean 定义
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)

但我可以看到 myServiceGateway() 方法只被调用一次,尽管有原型作用域,应用程序启动仍然失败,并显示错误消息,建议使用原型作用域 - 实际上非常混乱 ;- )

基于我也试过:

@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)

应用程序启动,但响应从未到达网关 widgetTransformer 之后的步骤。 (更奇怪的是,恰好 widgetTransformer 被跳过:在 flow1 中,结果是未转换的网关响应,而在 flow2 中,未转换的消息在 after widgetTransformer,即 servicePojo)。从消息生成器中创建代理似乎不是一个好主意。

我真的很想弄个水落石出。要求使用原型范围的异常消息是错误的还是我只是弄错了?如果我需要多个以相同方式设置的此类生产者,如何避免为消息生产者重复 bean 定义?

使用 spring-集成 5.0.9.

我不完全确定为什么 @Scope 不起作用,但这里有一个解决方法...

@SpringBootApplication
public class So52453934Application {

    public static void main(String[] args) {
        SpringApplication.run(So52453934Application.class, args);
    }

    @Autowired
    private HandlerConfig config;

    @Bean
    public IntegrationFlow flow1() {
        return f -> f.handle(this.config.myHandler())
                .handle(System.out::println);
    }

    @Bean
    public IntegrationFlow flow2() {
        return f -> f.handle(this.config.myHandler())
                .handle(System.out::println);
    }

    @Bean
    public ApplicationRunner runner() {
        return args -> {
            context.getBean("flow1.input", MessageChannel.class).send(new GenericMessage<>("foo"));
            context.getBean("flow2.input", MessageChannel.class).send(new GenericMessage<>("bar"));
        };
    }

}

@Configuration
class HandlerConfig {

    public AbstractReplyProducingMessageHandler myHandler() {
        return new AbstractReplyProducingMessageHandler() {

            @Override
            protected Object handleRequestMessage(Message<?> requestMessage) {
                return ((String) requestMessage.getPayload()).toUpperCase();
            }

        };
    }

}

即按照@artem 的建议进行操作,但使用工厂方法注入bean。