【问题标题】:Spring Integration: reuse MessageProducer definitionSpring Integration:重用 MessageProducer 定义
【发布时间】:2018-09-22 21:47:00
【问题描述】:

我有一个经过精心设置的用于肥皂呼叫的出站网关 (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() 方法只被调用一次,尽管原型范围,并且应用程序启动仍然失败并显示建议使用原型范围的错误消息 - 实际上很混乱;-)

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

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

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

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

使用弹簧集成 5.0.9。

【问题讨论】:

    标签: spring-integration spring-integration-dsl


    【解决方案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。

    【讨论】:

    • 啊,将通用配置注入 bean 并从那里使用 myHandler() 方法。非常好,谢谢。但是关于错误消息,原型 bean 是否应该在这种情况下工作?我可以为此打开一个 Jira。
    • 我还不知道,这是否是 Spring Framework 中的错误,或者是否是 Java 配置的已知限制(在这种情况下,我们需要更改消息)。在 SpringOnePlatform 会议之后,我将在下周研究它。随意打开一个 JIRA 问题,所以我们不要忘记它。
    猜你喜欢
    • 2018-03-09
    • 2014-09-09
    • 1970-01-01
    • 1970-01-01
    • 2021-05-29
    • 1970-01-01
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多