Skip to content

使用 XPath 路由 XML 消息(Kotlin DSL 版)

NOTE

XPath 路由器的本质
XPath 路由器是 Spring Integration 中处理 XML 消息的核心组件,它根据 XPath 表达式 动态决定消息的路由路径,类似于邮局根据邮政编码分发邮件。

一、XPath 路由器基础

1.1 核心概念

1.2 基础配置示例

kotlin
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.integration.dsl.integrationFlow
import org.springframework.integration.xml.dsl.xml

@Configuration
class XPathRouterConfig {

    @Bean
    fun orderRoutingFlow() = integrationFlow("orderChannel") {
        // 使用XPath表达式路由消息
        route<Any>(xml.xpathRouter("/order/type")) {
            // 定义通道映射
            channelMapping("BOOK", "bookOrdersChannel")
            channelMapping("ELECTRONIC", "electronicOrdersChannel")
            // 默认通道
            defaultOutputChannel("defaultOrdersChannel")
        }
    }
}
kotlin
// [!code highlight:5-8] // 重点:路由规则定义
// [!code warning:10] // 注意:务必设置默认通道处理意外情况

二、路由模式详解

2.1 单通道路由

当 XPath 表达式返回单个值时,消息将路由到指定通道:

kotlin
@Bean
fun singleRouteFlow() = integrationFlow("inChannel") {
    route(xml.xpathRouter("/request/type")) {
        channelMapping("A", "channelA")
    }
}

2.2 多通道路由(接收者列表)

当 XPath 表达式返回多个值时,消息将广播到所有匹配通道:

xml
<!-- 原始XML配置 -->
<int-xml:xpath-router id="responderRouter" input-channel="orderChannel">
    <int-xml:xpath-expression expression="/request/responders"/>
</int-xml:xpath-router>
kotlin
// Kotlin DSL 等效实现
@Bean
fun multiRouteFlow() = integrationFlow("orderChannel") {
    // 返回节点集合时自动成为接收者列表
    route(xml.xpathRouter("/request/responders"))
}

TIP

多通道路由应用场景
适用于需要将同一消息广播到多个处理服务的场景,如订单处理时同时通知库存系统和物流系统。

2.3 带映射的多通道路由

当需要将 XPath 结果映射到实际通道名称时:

kotlin
@Bean
fun mappedRouteFlow() = integrationFlow("orderChannel") {
    route(xml.xpathRouter("/request/responderType")) {
        channelMapping("RESP_A", "channelA")
        channelMapping("RESP_B", "channelB")
        // 显式启用接收者列表模式
        resolutionRequired(false)  // [!code warning] // 关键配置:允许返回多个值
    }
}

重要陷阱

如果未设置 resolutionRequired = false,当 XPath 返回多个值时系统将抛出异常。此配置明确告知路由器允许多值返回。

三、XPath 评估类型深度解析

3.1 节点集 vs 字符串评估

3.2 字符串评估模式

当表达式返回字符串而非节点集时:

kotlin
@Bean
fun stringEvalFlow() = integrationFlow("xpathStringChannel") {
    route(xml.xpathRouter("name(./node())") {
        // 启用字符串评估模式
        evaluateAsString = true
    }) {
        channelMapping("order", "orderProcessingChannel")
        channelMapping("invoice", "billingChannel")
    }
}

CAUTION

字符串评估的注意事项
当表达式选择多个节点时,string() 函数只返回第一个节点的值。如果需要处理所有节点,应使用节点集模式。

四、高级配置技巧

4.1 自定义 XML 转换器

虽然 Spring 提供了默认转换器,但可自定义处理逻辑:

kotlin
@Bean
fun customConverterRouter() = integrationFlow("customConvertChannel") {
    route(xml.xpathRouter("/data/type") {
        // 注入自定义转换器
        converter = MyCustomConverter()  
    }) {
        // ...通道映射配置
    }
}

class MyCustomConverter : XmlPayloadConverter {
    override fun convertToSource(payload: Any): Source {
        // 自定义转换逻辑
        return DOMSource(/* 自定义DOM处理 */)
    }
}

最佳实践建议

优先考虑使用上游转换器预处理 XML 数据,保持路由器逻辑简洁:

kotlin
integrationFlow("inputChannel") {
    transform(xml.unmarshallingTransformer())  // 先转换格式
    handle(/* 其他处理 */)
    // 再进入路由
    channel("routingChannel")
}

4.2 综合配置示例

kotlin
@Bean
fun comprehensiveRouter() = integrationFlow("mainInputChannel") {
    route(xml.xpathRouter("/transaction/type") {
        evaluateAsString = false  // 默认值,显式设置更清晰
        converter = CustomXmlConverter()
    }) {
        channelMapping("PAYMENT", "paymentProcessingChannel")
        channelMapping("REFUND", "refundChannel")
        defaultOutputToParent(true)  // 无匹配时返回给调用方
        resolutionRequired(false)    // 允许多值返回
    }
}

五、常见问题解决方案

5.1 类型转换异常

问题ClassCastException: String cannot be cast to NodeList
原因:表达式返回字符串但未设置 evaluateAsString=true
解决

kotlin
route(xml.xpathRouter("name(./node())") {
    evaluateAsString = true  // [!code ++] // 添加此行
})

5.2 路由遗漏问题

问题:部分消息未被路由到任何通道
原因:未设置默认通道且存在未匹配值
解决

kotlin
route(/*...*/) {
    defaultOutputChannel("deadLetterChannel")  
}

5.3 性能优化方案

kotlin
@Bean
fun optimizedRouter() = integrationFlow {
    route(xml.xpathRouter("/largeDoc/section") {
        // 启用编译缓存提升性能
        xpathExpression = XPathExpressionFactory
            .createXPathExpression("/largeDoc/section")
            .apply { compile() }  
    })
}

六、最佳实践总结

  1. 评估类型选择

    • 节点操作 ➜ evaluateAsString=false(默认)
    • 文本提取 ➜ evaluateAsString=true
  2. 通道映射策略

    kotlin
    // 清晰的映射声明优于隐式转换
    channelMapping("TYPE_A", "channelA")  
  3. 错误处理

    kotlin
    route {
        defaultOutputChannel("errorChannel")  
        sendTimeout = 5000  // 设置超时防止阻塞
    }
  4. 组合使用模式

IMPORTANT

生产环境建议
在正式环境中,始终配置 metrics 监控路由决策:

kotlin
@Bean
fun monitoringConfig() {
    Micrometer.metrics(/* 配置监控 */)
}

通过本教程,您已掌握使用 Kotlin DSL 配置 XPath 路由器的核心技能。实际应用中,建议结合 Spring Integration 的监控功能错误处理机制构建健壮的 XML 处理流水线。