Мне нужно использовать PayloadTypeRouter и я хочу отправить маршрутизируемое сообщение прямо в Transformer, Filter или ServiceActivator. Все должно быть настроено с помощью Kotlin DSL (или Java DSL).

В настоящее время красивый фрагмент кода выглядит так:

    @Bean
    fun routeAzureC2DMessage() = integrationFlow {
        channel(IoTHubChannelNames.IOTHUB_TO_DEVICE_CHANNEL)
        route<IotHubC2DRequestMessage<IotHubC2DRequest>> {
            when (it.payload) {
                is IotHubDesiredPropertyUpdate -> IoTHubChannelNames.DESIRED_PROPERTY_UPDATE
                is IotHubMessageToDevice -> IoTHubChannelNames.MESSAGE_TO_DEVICE
            }
        }
    }

А затем продолжил (одна сторона маршрута)

    @Bean
    fun processMessageToDevice() = integrationFlow {
        channel(IoTHubChannelNames.MESSAGE_TO_DEVICE)
        filter(StructureFilter())
        transform(MessageTransformer())
        channel(SharedChannelNames.CLOUD2DEVICE)
    }

Я хочу избавиться от ненужного канала IoTHubChannelNames.MESSAGE_TO_DEVICE. Я пробовал несколько подходов и в другой части проекта выяснил что-то вроде этого (Java DSL)

IntegrationFlows
            .from(channelName)
            .route({ message: IotHubMessage -> message.javaClass }) { router: RouterSpec<Class<*>?, MethodInvokingRouter?> ->
                router
                    .subFlowMapping(DeviceToCloudMessage::class.java) {
                        it.handle(gateway, "handleD2CMessage")
                    }
                    .subFlowMapping(DeviceTwinUpdateMessage::class.java) {
                        it.handle(gateway, "handleDeviceTwinReportedProperty")
                    }
            }
            .get()

Является ли subFlowMapping единственным способом избавиться от каналов между ними? Мне нужно решение, в котором я все еще могу использовать when (it.payload), а затем вместо имени канала / канала мог бы вернуть новое integrationFlow или какую-либо другую форму определения потока.

2
stk 9 Фев 2021 в 19:49

1 ответ

Лучший ответ

Единственное решение на данный момент действительно через API:

inline fun <reified P, T> route(
        crossinline function: (P) -> T,
        crossinline configurer: KotlinRouterSpec<T, MethodInvokingRouter>.() -> Unit) {

То, что вы спрашиваете с синтаксисом when(...) is, в настоящее время не поддерживается.

Не стесняйтесь поднимать вопрос GH по этому вопросу и делиться как можно более подробными сведениями о том, что это такое с точки зрения Kotlin и как его можно использовать в Spring Integration DSL.

ОБНОВЛЕНИЕ

С другой стороны, с текущей поддержкой Kotlin все не так плохо:

            route<Int, Boolean>({ it % 2 == 0 }) {
                subFlowMapping(true) { handle<Int> { p, _ -> p * 2 } }
                subFlowMapping(false) { handle<Int> { p, _ -> p * 3 } }
            }

Таким образом, аргумент метода route() - это when(), а subFlowMapping() - is с выходом -> в качестве результата построителя integrationFlow. Так что, вероятно, мы не будем преследовать Kotlin when(), который не принесет нам особой выгоды, если мы не проиграем subFlowMapping в пользу оператора -> ...

ОБНОВЛЕНИЕ 2

Поразмыслив над этим и подыскивая возможное решение, я должен отозвать свой запрос на этот when() - добрый запрос функции.

Основная проблема в том, что IntegrationFlow вместе со всей его конфигурацией должен быть зарегистрирован в контексте приложения заранее, перед использованием. То, что вы спрашиваете с when() в исходной функции маршрутизатора, это не то, что фреймворк может обнаружить и обработать за вас. Эта функция не является частью структуры, которая берет на себя ответственность за полученный результат.

Что ж, мы могли бы проверить возврат IntegrationFlow, чтобы принять решение, как вызвать, но нет гарантии, что поток, который вы собираетесь вернуть из этой функции, является зарегистрированным bean-компонентом. И когда мы действительно регистрируем его и пытаемся использовать в такой функции, это не отличается от того, что у нас есть до сих пор с каналами, возвращающимися из этой функции, и их отображением в каком-то потоковом компоненте где-то.

Мы можем зарегистрировать IntegrationFlow как bean-компонент автоматически в некоторых инструкциях, разработанных для этой цели, например subFlowMapping(). В любом случае это делается только один раз, на этапе настройки. Но это не очень хорошо, когда код конечного пользователя возвращает потоки во время выполнения. Лучше возвращать каналы или какие-то другие ключи, где у нас есть отображение для существующих потоков.

Я лично предпочитаю не игнорировать абстракцию MessageChannel и использовать ее, когда мне нужно распределить логику между разными потоками. Код в едином потоке выглядит более чистым, когда он линейен и представляет собой единую логическую единицу работы. Однако другие потоки могут быть повторно использованы в другой логике. Все, что мне нужно, это указать на каналы ввода этих потоков из других мест!

Тем не менее, моя основная мысль такова: мы должны зарегистрироваться и IntegrationFlow, прежде чем мы собираемся отправить ему сообщение.

2
Artem Bilan 10 Фев 2021 в 15:13