Spring IntegrationFlow 实施中的 ClassCastException

ClassCastException in Spring IntegrationFlow Implementation

我目前有一个 IntegrationFlow 实现,它利用服务 class 来实现流程要执行的所有所需功能。像这样...

@Service
public class FlowService {

    public Message<String> removeLineFeeds(Message<String> message) {
        return MessageBuilder
                .withPayload(StringUtils.remove(message.getPayload(), StringUtils.LF))
                .copyHeadersIfAbsent(message.getHeaders())
                .build();
    }

}

@Configuration
@EnableIntegration
public class FlowConfiguration {

    @Autowired
    private FlowService flowService;

    @Bean
    public IntegrationFlow flow() {
        return IntegrationFlows
                .from("inputChannel")
                .transform(flowService, "removeLineFeeds")
                .get();
    }
}

上面的实现完全按照预期工作,但我希望 improve/modify 实现利用 Java 8/Lambdas 的强大功能,使其看起来像这样...

@Bean
public IntegrationFlow flow() {
    return IntegrationFlows
            .from("inputChannel")
            .transform(flowService::removeLineFeeds)
            .get();
}

不幸的是,当以这种方式实现时,流将在处理消息时抛出 ClassCastException。我已经尝试了一些目前在线存在的不同提议的解决方案,但其中 none 似乎可以解决问题。无论使用何种 IntegrationFlow 方法(转换、过滤等),我都遇到了类似的问题。

当前实现需要更改哪些内容才能允许在 IntegrationFlow 方法中使用 flowService::removeLineFeeds

编辑:每个 ARTEM 的回应

IntegrationFlow 中的一个简单转换器似乎可以解决问题。我当前的实现似乎将消息作为 Message<byte[]> 而不是我期望的 Message<String> 传递。有关详细信息,请参阅下面 Artem 的完整回复。

@Bean
public IntegrationFlow flow() {
    return IntegrationFlows
            .from("inputChannel")
            .convert(String.class)
            .transform(flowService::removeLineFeeds)
            .get();
}

关键是lambda必须对应一些函数式接口。 在 transform() 的情况下,它是 GenericTransformer<S, T>。事实上,你的 Message<String> removeLineFeeds(Message<String> message) 满足这样的契约。如果你只处理有效载荷,它会很好用:

public String removeLineFeeds(String message) {
    return StringUtils.remove(message.getPayload(), StringUtils.LF);
}

只是因为当目标实现中的所有通用信息在运行时被删除时,我们无法猜测您想要处理整个 Message<?>,所以框架只向您的 lambda 传播有效载荷.您的 String 无法转换为 Message,因此 ClassCastException.

为了解决问题并模拟 Java 泛型系统,我们建议使用具有显式预期类型的​​重载方法:

/**
 * Populate the {@link MessageTransformingHandler} instance for the provided
 * {@link GenericTransformer} for the specific {@code payloadType} to convert at
 * runtime.
 * @param payloadType the {@link Class} for expected payload type. It can also be
 * {@code Message.class} if you wish to access the entire message in the transformer.
 * Conversion to this type will be attempted, if necessary.
 * @param genericTransformer the {@link GenericTransformer} to populate.
 * @param <P> the payload type - 'transform from' or {@code Message.class}.
 * @param <T> the target type - 'transform to'.
 * @return the current {@link BaseIntegrationFlowDefinition}.
 * @see MethodInvokingTransformer
 * @see LambdaMessageProcessor
 */
public <P, T> B transform(Class<P> payloadType, GenericTransformer<P, T> genericTransformer) {

因此,您的配置应如下所示:

.transform(Message.class, flowService::removeLineFeeds)

这样我们就可以说出我们想要获取完整消息以供我们的函数处理的框架。

无论如何,我更喜欢第一个带有 payload 的变体:框架会照顾您将请求 headers 处理成回复消息。

在文档中查看更多信息:https://docs.spring.io/spring-integration/reference/html/dsl.html#java-dsl-class-cast