Spring Integration Samples(示例)

从 Spring Integration 2.0 开始,Spring Integration 发行版不再包含样本。 相反,我们转向了一种更简单的合作模式,这种模式应该促进更好的社区参与,理想情况下,更多的贡献。 示例现在具有专用的 GitHub 存储库。 示例开发也有其自己的生命周期,它不依赖于框架版本的生命周期,尽管出于兼容性原因,存储库仍与每个主要版本一起标记。

对社区的最大好处是,我们现在可以添加更多示例并立即将它们提供给您,而无需等待下一个版本。 拥有自己的不与实际框架绑定的 GitHub 存储库也是一个很大的好处。 现在,您有一个专用的位置来建议示例以及报告现有示例的问题。 您也可以将样本作为拉取请求提交给我们。 如果我们相信您的示例增加了价值,我们将非常乐意将其添加到“示例”存储库中,并正确地注明您是作者。

从哪里获得样品

Spring Integration Samples项目托管在GitHub上。 若要签出或克隆示例,必须在系统上安装 Git 客户端。 有几个基于 GUI 的产品可用于许多平台(例如用于 Eclipse IDE 的EGit)。 一个简单的谷歌搜索可以帮助你找到它们。 您还可以使用Git的命令行界面。

如果需要有关如何安装或使用 Git 的更多信息,请访问:https://git-scm.com/。

要使用 Git 命令行工具克隆(签出)Spring 集成示例存储库,请发出以下命令:

$ git clone https://github.com/spring-projects/spring-integration-samples.git

上述命令将整个示例存储库克隆到发出该命令的工作目录中命名的目录中。 由于示例存储库是实时存储库,因此可能需要执行定期拉取(更新)以获取新示例和对现有示例的更新。 为此,请发出以下命令:??spring-integration-samples????git????git pull??

$ git pull提交样品或样品请求

您可以提交新样品和样品请求。 我们非常感谢任何改进样本的努力,包括分享好的想法。

如何贡献自己的样本?

GitHub 用于社交编码:如果您想将自己的代码示例提交到 Spring Integration Samples(Spring Integration Samples)项目,我们鼓励通过来自此存储库分支的拉取请求做出贡献。 如果您想以这种方式贡献代码,请尽可能参考GutHub 问题,其中提供了有关您的示例的一些详细信息。

签署贡献者许可协议

非常重要:在我们接受您的 Spring 集成示例之前,我们需要您签署 SpringSource 贡献者许可协议 (CLA)。 签署贡献者协议并不授予任何人对主仓库的提交权限,但这确实意味着我们可以接受您的贡献,如果我们这样做,您将获得作者信用。 要阅读并签署 CLA,请转到:

https://support.springsource.com/spring_committer_signup

从“项目”下拉列表中,选择“Spring 集成”。 项目负责人是Artem Bilan。

代码贡献流程

有关实际的代码贡献过程,请阅读 Spring 集成的贡献者指南。 他们也申请了样品项目。 您可以在以下位置找到它们https://github.com/spring-projects/spring-integration/blob/main/CONTRIBUTING.md

此过程可确保每次提交都经过同行评审。 事实上,核心提交者遵循完全相同的规则。 我们非常感谢您的弹簧集成示例!

样品请求

如前所述,Spring 集成示例项目使用 GitHub 问题作为错误跟踪系统。 要提交新的示例请求,请访问https://github.com/spring-projects/spring-integration-samples/issues。

样品结构

从 Spring Integration 2.0 开始,示例的结构发生了变化。 通过计划更多样本,我们意识到并非所有样本都有相同的目标。 它们都有一个共同的目标,即向您展示如何应用和使用 Spring 集成框架。 但是,它们的不同之处在于,一些示例专注于技术用例,而其他示例则侧重于业务用例。 此外,一些示例还展示了可用于解决某些方案(技术和业务)的各种技术。 新的样品分类使我们能够根据每个样品解决的问题更好地组织它们,同时为您提供一种更简单的方式来找到适合您需求的样品。

目前,有四类。 在示例存储库中,每个类别都有自己的目录,该目录以类别名称命名:

基本 (??samples/basic??)

这是一个很好的起点。 此处的示例是出于技术动机的,并演示了配置和代码方面的最低限度。 这些应该通过向您介绍 Spring 集成的基本概念、API 和配置以及企业集成模式 (EIP) 来帮助您快速入门。 例如,如果您正在寻找有关如何实现服务激活器并将其连接到消息通道、如何使用消息传递网关作为消息交换的外观或如何开始使用 MAIL、TCP/UDP 或其他模块的答案,那么这是查找良好示例的正确位置。 底线是开始的地方。??samples/basic??

中间(??samples/intermediate??)

此类别面向已经熟悉 Spring 集成框架(除了入门之外)但在解决切换到消息传递架构后可能遇到的更高级技术问题时需要更多指导的开发人员。 例如,如果您正在寻找有关如何处理各种消息交换方案中的错误或如何正确配置聚合器以应对某些消息永远不会到达聚合的情况的答案,或者超出特定组件的基本实现和配置并暴露“其他”类型问题的任何其他问题, 这是查找此类样本的正确位置。

高级 (??samples/advanced??)

此类别针对非常熟悉 Spring 集成框架但希望通过使用 Spring 集成的公共 API 对其进行扩展以满足特定自定义需求的开发人员。 例如,如果您正在寻找演示如何实现自定义通道或使用者(基于事件或基于轮询)的示例,或者您正在尝试找出在 Spring 集成 Bean 解析器层次结构之上实现自定义 Bean 解析器的最合适方法(可能在为自定义组件实现您自己的命名空间和模式时), 这是正确的地方。 在这里,您还可以找到有助于您进行适配器开发的示例。 Spring 集成附带了一个广泛的适配器库,可让您将远程系统与 Spring 集成消息传递框架连接起来。 但是,您可能需要与核心框提供适配器的系统集成。 如果是这样,您可能会决定实现自己的(请考虑贡献它)。 此类别将包括演示如何操作的示例。

应用 (??samples/applications??)

此类别面向对消息驱动架构和 EIP 有良好理解,并且对 Spring 和 Spring 集成有高于平均水平的理解的开发人员和架构师,他们正在寻找解决特定业务问题的示例。 换句话说,此类别中示例的重点是业务用例,以及如何通过消息驱动的体系结构和 Spring 集成来解决它们。 例如,如果您想了解如何使用 Spring 集成实施和自动化贷款经纪人或旅行社流程,这是查找这些类型示例的正确位置。

Spring Integration是一个社区驱动的框架。 因此,社区参与很重要。 这包括示例。 如果您找不到所需的内容,请告诉我们!

样品

目前,Spring 集成附带了相当多的示例,您只能期待更多。 为了帮助您更好地浏览它们,每个示例都附带了自己的文件,其中包含有关示例的多个详细信息(例如,它解决了哪些 EIP 模式、它试图解决什么问题、如何运行示例以及其他详细信息)。 但是,某些示例需要更详细的,有时甚至是图形化的解释。 在本节中,您可以找到我们认为需要特别注意的样品的详细信息。??readme.txt??

贷款经纪人

本节介绍 Spring 集成示例中包含的贷款经纪人示例应用程序。 此示例的灵感来自 Gregor Hohpe 和 Bobby Woolf 的书《企业集成模式》中的一个示例。

下图显示了整个过程:

图1.贷款经纪人示例

EIP 架构的核心是管道、过滤器,当然还有消息等非常简单但功能强大的概念。 终结点(筛选器)通过通道(管道)相互连接。 生成终结点将消息发送到通道,使用终结点检索消息。 此体系结构旨在定义各种机制,这些机制描述如何在终结点之间交换信息,而不了解这些终结点是什么或它们正在交换哪些信息。 因此,它提供了一个非常松散耦合和灵活的协作模型,同时还将集成问题与业务问题分离。 EIP 通过进一步定义以下内容来扩展此体系结构:

管道类型(点对点通道、发布-订阅通道、通道适配器等)围绕筛选器如何与管道协作的核心筛选器和模式(消息路由器、拆分器和聚合器、各种消息转换模式等)

EIP 书籍的第 9 章很好地描述了此用例的细节和变体,但以下是简要摘要: 在购买最佳贷款报价时,消费者订阅贷款经纪人的服务,该经纪人处理以下详细信息:

消费者预筛选(例如,获取和审查消费者的信用记录)确定最合适的银行(例如,根据消费者的信用记录或分数)向每个选定的银行发送贷款报价请求收集各银行的回复根据消费者的要求过滤响应并确定最佳报价。将贷款报价传回给消费者。

获得贷款报价的实际过程通常要复杂一些。 但是,由于我们的目标是演示如何在Spring Integration中实现和实现企业集成模式,因此用例已简化为仅关注流程的集成方面。 这不是试图在消费金融方面给你建议。

通过聘请贷款经纪人,消费者与贷款经纪人的运营细节隔离开来,每个贷款经纪人的操作可能会相互推迟以保持竞争优势,因此我们组装和实施的任何内容都必须灵活,以便可以快速轻松地引入任何更改。

贷款经纪人样本实际上并没有与任何“虚构的”银行或信用局交谈。 这些服务被排除在外。

我们的目标是组装、编排和测试整个流程的集成方面。 只有这样,我们才能开始考虑将这些流程连接到真正的服务。 此时,无论特定贷款经纪人与多少银行打交道或使用的通信介质(或协议)类型(JMS、WS、TCP 等)与这些银行进行通信,组装的过程及其配置都不会更改。

设计

在分析前面列出的六个需求时,您可以看到它们都是集成问题。 例如,在消费者预筛选步骤中,我们需要收集有关消费者和消费者愿望的额外信息,并用额外的元信息丰富贷款请求。 然后,我们必须过滤此类信息以选择最合适的银行列表,依此类推。 扩充、筛选和选择都是 EIP 以模式形式定义解决方案的集成问题。 Spring Integration 提供了这些模式的实现。

下图显示了消息网关的表示形式:

图2.消息网关

消息传递网关模式提供了一种访问消息传递系统(包括我们的贷款代理)的简单机制。 在 Spring Integration 中,您可以将网关定义为一个普通的旧 Java 接口(您不需要提供实现),使用 XML 元素或 Java 中的注释对其进行配置,并像使用任何其他 Spring Bean 一样使用它。 Spring 集成通过生成消息(有效负载映射到方法的输入参数)并将其发送到指定通道,负责将方法调用委派和映射到消息传递基础结构。 下面的示例演示如何使用 XML 定义此类网关:??<gateway>??

<int:gateway id=”loanBrokerGateway” default-request-channel=”loanBrokerPreProcessingChannel” service-interface=”org.springframework.integration.samples.loanbroker.LoanBrokerGateway”> <int:method name=”getBestLoanQuote”> <int:header name=”RESPONSE_TYPE” value=”BEST”/> </int:method></int:gateway>

我们当前的网关提供了两种可以调用的方法。 一个返回最佳单引号,另一个返回所有引号。 不知何故,在下游,我们需要知道呼叫者需要什么类型的回复。 在消息传递体系结构中实现此目的的最佳方法是使用一些描述意图的元数据来丰富消息的内容。 内容丰富器是解决此问题的模式之一。 为了方便起见,Spring 集成确实提供了一个单独的配置元素,以使用任意数据丰富消息头(稍后描述) 但是,由于该元素负责构造初始消息,因此它包括使用任意消息标头丰富新创建的消息的功能。 在我们的示例中,我们添加一个标头,其值为每当调用该方法时。 对于其他方法,我们不添加任何标头。 现在我们可以检查下游是否存在此标头。 根据它的存在和值,我们可以确定呼叫者想要的回复类型。??gateway????RESPONSE_TYPE????BEST????getBestQuote()??

根据用例,我们还知道需要执行一些预筛选步骤,例如获取和评估消费者的信用评分,因为一些顶级银行只接受满足最低信用评分要求的消费者的报价请求。 因此,如果在将消息转发给银行之前用这些信息来丰富信息,那就太好了。 如果需要完成几个过程来提供这种元信息,可以将这些过程分组到一个单元中,那也很好。 在我们的用例中,我们需要确定信用评分,并根据信用评分和一些规则,选择要向其发送报价请求的消息渠道(银行渠道)列表。

组合消息处理器

组合消息处理器模式描述了有关构建端点的规则,这些端点保持对消息流(由多个消息处理器组成)的控制。 在 Spring 集成中,组合的消息处理器模式由元素实现。??<chain>??

下图显示了链模式:

图3.链

上图显示,我们有一个链,其中包含一个内部标头丰富器元素,该元素使用标头和值(由对信用服务的调用确定 – 由“creditBureau”名称标识的简单POJO弹簧豆)进一步丰富消息的内容。 然后它委托给消息路由器。??CREDIT_SCORE??

下图显示了消息路由器模式:

图4.消息路由器

Spring 集成提供了几种消息路由模式的实现。 在这种情况下,我们使用一个路由器,它根据评估一个表达式(在 Spring 表达式语言中)来确定通道列表,该表达式查看信用评分(在上一步中确定),并根据信用评分的值从 bean 中选择其值为 or 的通道列表。 选择通道列表后,消息将路由到这些通道。??Map????id????banks????premier????secondary??

现在,贷款经纪人需要做的最后一件事是从银行接收贷款报价,按消费者汇总(我们不想显示从一个消费者到另一个消费者的报价),根据消费者的选择标准(单一最佳报价或所有报价)组装响应并将回复发送给消费者。

下图显示了消息聚合器模式:

图5.消息聚合器

聚合器模式描述将相关消息分组到单个消息中的终结点。 可以提供标准和规则来确定聚合和关联策略。 Spring 集成提供了聚合器模式的几种实现以及方便的基于命名空间的配置。

以下示例演示如何定义聚合器:

<int:aggregator id=”quotesAggregator” input-channel=”quotesAggregationChannel” method=”aggregateQuotes”> <beans:bean class=”org.springframework.integration.samples.loanbroker.LoanQuoteAggregator”/></int:aggregator>

我们的贷款经纪人用元素定义了一个“quotesAggregator”bean,它提供了一个默认的聚合和关联策略。 默认关联策略根据标头关联消息(请参阅EIP 手册中的关联标识符模式)。 请注意,我们从未提供此标头的值。 它是由路由器在早些时候为每个银行通道生成单独的消息时自动设置的。??<aggregator>????correlationId??

一旦消息关联起来,它们就会发布到实际的聚合器实现中。 尽管 Spring Integration 提供了一个默认的聚合器,但它的策略(从所有消息中收集有效负载列表,并以此列表作为其有效负载构建一条新消息)不能满足我们的要求。 在消息中包含所有结果是一个问题,因为我们的使用者可能需要单个最佳报价或所有报价。 为了传达消费者的意图,在流程的早期,我们设置了标头。 现在我们必须评估此标头并返回所有报价(默认聚合策略将起作用)或最佳报价(默认聚合策略不起作用,因为我们必须确定哪个贷款报价是最好的)。??RESPONSE_TYPE??

在更现实的应用程序中,选择最佳报价可能基于可能影响聚合器实现和配置复杂性的复杂标准。 不过,就目前而言,我们正在简化它。 如果消费者想要最好的报价,我们选择利率最低的报价。 为此,该类按利率对所有报价进行排序并返回第一个报价。 该类实现基于费率属性比较报价。 创建响应消息后,该消息将发送到启动该过程的消息网关的缺省回复通道(从而发送到使用者)。 我们的消费者得到了贷款报价!??LoanQuoteAggregator????LoanQuote????Comparable??

总之,基于POJO(即现有或遗留)逻辑和轻量级,可嵌入的消息传递框架(Spring Integration)组装了一个相当复杂的过程,该框架具有松散耦合的编程模型,旨在简化异构系统的集成,而无需重量级的类似ESB的引擎或专有的开发和部署环境。 作为开发人员,您不需要仅仅因为有集成问题而将 Swing 或基于 Console 的应用程序移植到类似 ESB 的服务器上或实现专有接口。

此示例和本节中的其他示例基于企业集成模式构建。 您可以将它们视为解决方案的“构建基块”。 它们不是完整的解决方案。 所有类型的应用程序(无论是否基于服务器)都存在集成问题。 我们的目标是使集成应用程序不需要更改设计、测试和部署策略。

咖啡厅样本

本节介绍 Spring 集成示例中包含的咖啡馆示例应用程序。 这个样本的灵感来自Gregor Hohpe的Ramblings中的另一个样本。

域是咖啡馆的域,下图描述了基本流程:

图6.咖啡厅样品

对象可能包含多个 . 下订单后,拆分器会将复合订单消息分解为每种饮料的单个消息。 然后,每个都由路由器处理,路由器确定饮料是热的还是冷的(通过检查对象的“isIced”属性)。 准备每种饮料,但热饮和冷饮准备通过两种不同的方法处理:“准备热饮”和“准备冷饮”。 然后将准备好的饮料发送到它们被聚合成一个对象的地方。??Order????OrderItems????OrderItem????Barista????Waiter????Delivery??

以下清单显示了 XML 配置:

<?xml versinotallow=”1.0″ encoding=”UTF-8″?><beans:beans xmlns:int=”http://www.springframework.org/schema/integration” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xmlns:beans=”http://www.springframework.org/schema/beans” xmlns:int-stream=”http://www.springframework.org/schema/integration/stream” xsi:schemaLocation=”http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/stream https://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd”> <int:gateway id=”cafe” service-interface=”o.s.i.samples.cafe.Cafe”/> <int:channel id=”orders”/> <int:splitter input-channel=”orders” ref=”orderSplitter” method=”split” output-channel=”drinks”/> <int:channel id=”drinks”/> <int:router input-channel=”drinks” ref=”drinkRouter” method=”resolveOrderItemChannel”/> <int:channel id=”coldDrinks”><int:queue capacity=”10″/></int:channel> <int:service-activator input-channel=”coldDrinks” ref=”barista” method=”prepareColdDrink” output-channel=”preparedDrinks”/> <int:channel id=”hotDrinks”><int:queue capacity=”10″/></int:channel> <int:service-activator input-channel=”hotDrinks” ref=”barista” method=”prepareHotDrink” output-channel=”preparedDrinks”/> <int:channel id=”preparedDrinks”/> <int:aggregator input-channel=”preparedDrinks” ref=”waiter” method=”prepareDelivery” output-channel=”deliveries”/> <int-stream:stdout-channel-adapter id=”deliveries”/> <beans:bean id=”orderSplitter” class=”org.springframework.integration.samples.cafe.xml.OrderSplitter”/> <beans:bean id=”drinkRouter” class=”org.springframework.integration.samples.cafe.xml.DrinkRouter”/> <beans:bean id=”barista” class=”o.s.i.samples.cafe.xml.Barista”/> <beans:bean id=”waiter” class=”o.s.i.samples.cafe.xml.Waiter”/> <int:poller id=”poller” default=”true” fixed-rate=”1000″/></beans:beans>

每个消息端点都连接到输入通道和/或输出通道。 每个端点管理自己的生命周期(默认情况下,端点在初始化时自动启动,为防止这种情况,请添加值为 ) 的属性。 最重要的是,请注意,这些对象是具有强类型方法参数的简单 POJO。 以下示例显示了拆分器:??auto-startup????false??

public class OrderSplitter { public List<OrderItem> split(Order order) { return order.getItems(); }}

对于路由器,返回值不必是实例(尽管可以是)。 在此示例中,将返回保存通道名称的值,如以下清单所示。??MessageChannel????String??

public class DrinkRouter { public String resolveOrderItemChannel(OrderItem orderItem) { return (orderItem.isIced()) ? “coldDrinks” : “hotDrinks”; }}

现在,回到 XML,您可以看到有两个元素。 其中每个都委托给同一个实例,但使用不同的方法(或),每个方法对应于已路由订单项的两个通道之一。 下面的清单显示了 Barista 类(其中包含 和 方法)??<service-activator>????Barista????prepareHotDrink????prepareColdDrink????prepareHotDrink????prepareColdDrink??

public class Barista { private long hotDrinkDelay = 5000; private long coldDrinkDelay = 1000; private AtomicInteger hotDrinkCounter = new AtomicInteger(); private AtomicInteger coldDrinkCounter = new AtomicInteger(); public void setHotDrinkDelay(long hotDrinkDelay) { this.hotDrinkDelay = hotDrinkDelay; } public void setColdDrinkDelay(long coldDrinkDelay) { this.coldDrinkDelay = coldDrinkDelay; } public Drink prepareHotDrink(OrderItem orderItem) { try { Thread.sleep(this.hotDrinkDelay); System.out.println(Thread.currentThread().getName() + ” prepared hot drink #” + hotDrinkCounter.incrementAndGet() + ” for order #” + orderItem.getOrder().getNumber() + “: ” + orderItem); return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(), orderItem.isIced(), orderItem.getShots()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; } } public Drink prepareColdDrink(OrderItem orderItem) { try { Thread.sleep(this.coldDrinkDelay); System.out.println(Thread.currentThread().getName() + ” prepared cold drink #” + coldDrinkCounter.incrementAndGet() + ” for order #” + orderItem.getOrder().getNumber() + “: ” + orderItem); return new Drink(orderItem.getOrder().getNumber(), orderItem.getDrinkType(), orderItem.isIced(), orderItem.getShots()); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return null; } }}

从前面的代码摘录中可以看出,这些方法具有不同的延迟(准备热饮需要五倍的时间)。 这模拟以不同速率完成的工作。 当“main”方法运行时,它会循环 100 次,每次发送一个热饮和一个冷饮。 它实际上通过在接口上调用“placeOrder”方法来发送消息。 在较早的 XML 配置中,您可以看到指定了该元素。 这将触发实现给定服务接口并将其连接到通道的代理的创建。 通道名称在接口的注释中提供,如以下接口定义所示:??Barista????CafeDemo????Cafe????<gateway>????@Gateway????Cafe??

public interface Cafe { @Gateway(requestChannel=”orders”) void placeOrder(Order order);}

最后,看看方法本身:??main()????CafeDemo??

public static void main(String[] args) { AbstractApplicationContext context = null; if (args.length > 0) { context = new FileSystemXmlApplicationContext(args); } else { context = new ClassPathXmlApplicationContext(“cafeDemo.xml”, CafeDemo.class); } Cafe cafe = context.getBean(“cafe”, Cafe.class); for (int i = 1; i <= 100; i++) { Order order = new Order(i); order.addItem(DrinkType.LATTE, 2, false); order.addItem(DrinkType.MOCHA, 3, true); cafe.placeOrder(order); }}

要运行此示例以及其他八个示例,请参阅 主发行版目录中的(如本章开头?所述)。??README.txt????samples??

当你跑步时,你可以看到冷饮的初始准备速度比热饮快。 由于存在聚合器,冷饮实际上受到热饮制备速率的限制。 这是意料之中的,基于它们各自的 1000 毫秒和 5000 毫秒延迟。 但是,通过配置具有并发任务执行器的轮询器,可以显著更改结果。 例如,您可以对热饮咖啡师使用具有五个工作人员的线程池执行程序,同时保持冷饮咖啡师的原样。 下面的清单配置了这样的安排:??cafeDemo??

<int:service-activator input-channel=”hotDrinks” ref=”barista” method=”prepareHotDrink” output-channel=”preparedDrinks”/> <int:service-activator input-channel=”hotDrinks” ref=”barista” method=”prepareHotDrink” output-channel=”preparedDrinks”> <int:poller task-executor=”pool” fixed-rate=”1000″/> </int:service-activator> <task:executor id=”pool” pool-size=”5″/>

另请注意,每次调用都会显示工作线程名称。 您可以看到热饮是由任务执行器线程准备的。 如果提供更短的轮询间隔(如 100 毫秒),则可以看到它偶尔会通过强制任务计划程序(调用方)调用操作来限制输入。

除了试验轮询器的并发设置外,还可以添加“事务”子元素,然后引用上下文中的任何实例。??PlatformTransactionManager??

XML 消息传递示例

中的 XML 消息传递示例演示如何使用提供的一些处理 XML 有效负载的组件。 该示例使用处理表示为 XML 的书籍的订单的思想。??basic/xml??

此示例显示命名空间前缀可以是所需的任何内容。 虽然我们通常使用集成 XML 组件,但示例使用 . (是“集成”的缩写,是“Spring Integration”的缩写。??int-xml????si-xml????int????si??

首先,将订单拆分为多个消息,每个消息表示 XPath 拆分器组件中的单个订单项。 以下清单显示了拆分器的配置:

<si-xml:xpath-splitter id=”orderItemSplitter” input-channel=”ordersChannel” output-channel=”stockCheckerChannel” create-documents=”true”> <si-xml:xpath-expression expression=”/orderNs:order/orderNs:orderItem” namespace-map=”orderNamespaceMap” /> </si-xml:xpath-splitter>

然后,服务激活器将消息传递到股票检查器 POJO。 订单项目文档使用来自库存检查器的有关订单项目库存水平的信息进行扩充。 然后,此扩充的订单项消息用于路由消息。 如果订单物料有库存,则消息将路由到仓库。 以下清单配置了路由消息的 :??xpath-router??

<si-xml:xpath-router id=”inStockRouter” input-channel=”orderRoutingChannel” resolution-required=”true”> <si-xml:xpath-expression expression=”/orderNs:orderItem/@in-stock” namespace-map=”orderNamespaceMap” /> <si-xml:mapping value=”true” channel=”warehouseDispatchChannel”/> <si-xml:mapping value=”false” channel=”outOfStockChannel”/></si-xml:xpath-router>

当订单项没有库存时,使用 XSLT 将消息转换为适合发送给供应商的格式。 下面的清单配置 XSLT 转换器:

<si-xml:xslt-transformer input-channel=”outOfStockChannel” output-channel=”resupplyOrderChannel” xsl-resource=”classpath:org/springframework/integration/samples/xml/bigBooksSupplierTransformer.xsl”/>

奢侈地享受旅不问人,行随己意的潇洒。

Spring Integration Samples(示例)

相关文章:

你感兴趣的文章:

标签云: