@IdempotentReceiver注释不起作用

时间:2015-03-04 08:59:55

标签: spring spring-integration spring-java-config

我正在尝试使用Spring Integration 4.1.2参考指南中提到的@IdempotentReceiver注释和以下Java Config示例(修改为包含我的逻辑):

@Configuration
@EnableIntegration
public class IdempotentReceiverConfig {

    public static class MyPayload {
        public String transactionId;
        public String data;

        public MyPayload(final String transactionId, final String data) {
            this.transactionId = transactionId;
            this.data = data;
        }
    }

    public static class MyTransformer {
        public MyPayload process(final MyPayload input) {
            MyPayload newPayload = new MyPayload(input.transactionId, input.data.toUpperCase());
            return newPayload;
        }
    }

    @Bean
    public IdempotentReceiverInterceptor idempotentReceiverInterceptor() {
        ExpressionParser parser = new SpelExpressionParser();
        Expression exp = parser.parseExpression("payload.transactionId");
        MetadataStoreSelector selector = new MetadataStoreSelector(new ExpressionEvaluatingMessageProcessor<String>(exp));
        IdempotentReceiverInterceptor interceptor = new IdempotentReceiverInterceptor(selector);
        interceptor.setDiscardChannel(discardChannel());
        return interceptor;
    }

    @Bean
    @ServiceActivator(inputChannel = "inputChannel1")
    @IdempotentReceiver("idempotentReceiverInterceptor")
    public MessageHandler myService1() {
        ServiceActivatingHandler handler = new ServiceActivatingHandler(new MyTransformer(), "process");
        handler.setOutputChannel(outputChannel());
        return handler;
    }

    @Bean
    @ServiceActivator(inputChannel = "inputChannel2", outputChannel = "outputChannel")
    @IdempotentReceiver("idempotentReceiverInterceptor")
    public Transformer myService2() {
        return new MethodInvokingTransformer(new MyTransformer(), "process");
    }

    @Bean
    public MessageChannel inputChannel1() {
        return new DirectChannel();
    }

    @Bean
    public MessageChannel inputChannel2() {
        return new DirectChannel();
    }

    @Bean
    public MessageChannel outputChannel() {
        return new DirectChannel();
    }

    @Bean
    public MessageChannel discardChannel() {
        return new DirectChannel();
    }

    @Bean
    @ServiceActivator(inputChannel = "outputChannel")
    public LoggingHandler outputLoggingHandler() {
        LoggingHandler handler = new LoggingHandler("INFO");
        handler.setLoggerName("Success.LoggingHandler");
        handler.setExpression("'Message passed: ' + payload.data");
        return handler;
    }

    @Bean
    @ServiceActivator(inputChannel = "discardChannel")
    public LoggingHandler discardLoggingHandler() {
        LoggingHandler handler = new LoggingHandler("WARN");
        handler.setLoggerName("Fail.LoggingHandler");
        handler.setExpression("'Message discarded: ' + payload.data");
        return handler;
    }
}

但是,如果我使用ServiceActivatingHandler(inputChannel1),似乎不会将IdempotentReceiverInterceptor应用于端点。如果我更改为XML配置或使用Transformer(inputChannel2),程序运行正常。

我通过向inputChannel1和inputChannel2发送两条消息来测试上述内容:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(IdempotentReceiverConfig.class);
MessageChannel channel;
MyPayload payload = new MyPayload("1234", "testing");

channel = (MessageChannel) ctx.getBean("inputChannel1");
channel.send(new GenericMessage<MyPayload>(payload));
channel.send(new GenericMessage<MyPayload>(payload));

channel = (MessageChannel) ctx.getBean("inputChannel2");
channel.send(new GenericMessage<MyPayload>(payload));
channel.send(new GenericMessage<MyPayload>(payload));

结果是:

[main] [LoggingHandler] INFO   - Message passed: TESTING
[main] [LoggingHandler] INFO   - Message passed: TESTING

[main] [LoggingHandler] INFO   - Message passed: TESTING
[main] [LoggingHandler] WARN   - Message discarded: testing

所以我可以确认消息仍然转到MessageHandler以获取inputChannel1的情况。

经过一番研究,我发现Spring的AbstractMethodAnnotationPostProcessor.postProcess(...)方法具有以下处理IdempotentReceiver注释的逻辑:

if (AnnotatedElementUtils.isAnnotated(method, IdempotentReceiver.class.getName())   && !AnnotatedElementUtils.isAnnotated(method, Bean.class.getName())) {
...
}

在我看来,它同时不允许@Bean和@IdempotentReceiver。

是否有@IdempotentReceiver的工作样本?我错过了什么吗?

已编辑:使用完整配置更新了问题。 myService1()将返回一个ServiceActivatingHandler,它调用一个POJO转换器,myService2()使用一个Transformer。变换器只对payload.data字段执行简单的toUpperCase()操作,因此我们可以看看它是否已被调用。

1 个答案:

答案 0 :(得分:1)

@IdempotentReceiver@Bean IdempotentReceiverAutoProxyCreatorInitializerIdempotentReceiverIntegrationTests解析。

我们有一个关于此问题的测试案例 - myService()

从另一方面来说:你想如何确保它按预期工作?

请显示您的AbstractMessageProducingHandler内容。对于任何outputChannel,必须将AbstractMessageProducingHandler指定为outputChannel实施的设置者。与@ServiceActivator的<{1}}属性不同。

当我对此事做一些测试时,我会回复你。

<强>更新

我的测试用例证实一切正常:

    @Bean
    @ServiceActivator(inputChannel = "inputService")
    @IdempotentReceiver("idempotentReceiverInterceptor")
    public MessageHandler messageHandler() {
        return new MessageHandler() {

            @Override
            public void handleMessage(Message<?> message) throws MessagingException {
                if (message.getHeaders().containsKey(IntegrationMessageHeaderAccessor.DUPLICATE_MESSAGE)) {
                    throw new RuntimeException("Duplicate message");
                }
            }

        };

    }

我这样做:

Message<String> message = new GenericMessage<String>("bar");
this.inputService.send(message);
this.inputService.send(message);

如果idempotentReceiverInterceptor尚未应用,则第二个send通过,但不会。至少对我而言。

您是否可以最小化配置和测试以允许我重现您的问题?