ClassCastException в реализации Spring IntegrationFlow - PullRequest
0 голосов
/ 22 января 2020

В настоящее время у меня есть реализация IntegrationFlow, которая использует класс Service для реализации всех требуемых функций, которые будут выполняться потоком. Примерно так ...

@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();
    }
}

Вышеприведенная реализация работает точно так, как нужно, но я надеялся улучшить / изменить реализацию, чтобы использовать возможности Java 8 / Lambdas, чтобы она выглядела примерно так ...

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

К сожалению, при такой реализации поток будет выдавать ClassCastException всякий раз, когда обрабатывает сообщение. Я попробовал несколько различных предлагаемых решений, которые существуют в настоящее время в сети, но ни одно из них, похоже, не работает. Я сталкиваюсь с подобной проблемой независимо от используемого метода IntegrationFlow (transform, filter, et c.).

Что необходимо изменить в текущей реализации, чтобы разрешить использование flowService::removeLineFeeds в IntegrationFlow методы?

РЕДАКТИРОВАТЬ: ОТ ОТВЕТА АРТЕМ

Похоже, что простой преобразователь в IntegrationFlow сделал свое дело. Моя текущая реализация, казалось, передавала сообщение как Message<byte[]> вместо Message<String>, которое я ожидал. См. Полный ответ Артема ниже для более подробной информации.

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

1 Ответ

2 голосов
/ 22 января 2020

Дело в том, что лямбда должна соответствовать какому-то функциональному интерфейсу. В случае transform() это GenericTransformer<S, T>. Действительно, ваш Message<String> removeLineFeeds(Message<String> message) удовлетворяет такой контракт. И это будет хорошо работать, если вы имеете дело только с полезной нагрузкой:

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

Просто потому, что когда вся обобщенная информация c из целевой реализации стирается во время выполнения, мы не можем догадаться, что вы хотели бы иметь дело с все Message<?>, поэтому фреймворк распространяет на вашу лямбду только полезную нагрузку. Таким образом, ваш String не может быть приведен к Message, поэтому ClassCastException.

Чтобы решить проблему и смоделировать систему Java generics, мы предлагаем перегруженный метод с явным ожидаемым типом:

/**
 * 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: фреймворк позаботится о том, чтобы скопировать заголовки запроса в ответное сообщение.

См. Дополнительную информацию в Документах: https://docs.spring.io/spring-integration/reference/html/dsl.html#java -dsl класса литой

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...