Spring集成:重用MessageProducer定义

时间:2018-09-22 05:58:54

标签: spring-integration spring-integration-dsl

我有一个经过精心设置的肥皂呼叫的出站网关(MarshallingWebServiceOutboundGateway)。我需要从多个流中使用该网关定义。 问题spring-integration: MessageProducer may only be referenced once有点类似,但是这个问题是关于Spring集成协作者对Spring bean范围原型的正确使用。

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

@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不能是同一实例,因为该实例只有一个出站通道,并且不能属于两个不同的流。

在相关问题spring-integration: MessageProducer may only be referenced once中,@ 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()方法仅被调用一次,并且应用程序启动仍然失败,并显示错误消息,提示使用原型作用域-实际上相当混乱;-)< / p>

基于Mystery around Spring Integration and prototype scope,我也尝试过:

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

应用程序启动,但是响应永远不会到达网关widgetTransformer之后的位置。 (更奇怪的是,恰好跳过了widgetTransformer:在flow1中,结果是未转换的网关响应,而在flow2中,未转换的消息在之后 widgetTransformer,即servicePojo)。用消息生产者创建代理似乎不是一个好主意。

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

使用spring-integration 5.0.9。

1 个答案:

答案 0 :(得分:1)

我不确定@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。