开始之前
在本教程中,您将学习什么是Spring Integration ,如何使用它以及有助于解决哪些问题。 我们将从头开始构建一个示例应用程序,并演示Spring Integration的一些核心组件。 如果您不熟悉Spring,请查看我编写的另一本有关Spring的教程- 我们可以一起做一些Spring吗? 还要注意,您不需要任何特殊的工具,但是您可以使用IntelliJ IDEA或Spring Tool Suite来获得构建Spring Integration应用程序的最佳体验(使用STS可以获得一些精美的图表)。 您可以按照本教程逐步操作并自己从头开始创建应用程序,也可以继续从github获取代码:
此处下载资源: https : //github.com/vrto/spring-integration-invoices
无论您喜欢哪种方式,都该开始了!
发票处理申请书-功能说明
想象一下,您在某家公司工作,该公司会定期从各种承包商那里收到大量发票。 我们将建立一个能够接收发票,过滤掉相关发票,创建付款(本地或国外)并将其发送到某些银行服务的系统。 即使该系统非常幼稚,当然也不适合企业使用,我们仍将尝试在头脑中以良好的可伸缩性,灵活性和分离的设计来构建它。
在继续之前,您必须意识到一件事:Spring Integration是(不仅但主要是)关于消息传递的 。 Spring Integration基本上是嵌入式企业服务总线 ,可让您无缝地将业务逻辑连接到消息传递通道。 可以通过编程方式(通过Spring Integration API)或自动(通过框架本身–更高级别的解耦)来处理消息。 消息是跨渠道传播的东西。 消息具有标题和有效负载 –在我们的示例中,它们将是实际相关的内容(域类)。 让我们看一下下面的图片,它是系统的摘要,并遍历了重要的部分:
在图片上,您可以看到一个集成图,该图说明了我们的消息传递结构和系统的核心组件-它们用红色数字标记。 让我们来看一下(稍后我们将更详细地介绍每个组件):
- 发票网关 –这是我们放置新发票的地方,以便它们可以进入消息传递层
- 拆分器 -该系统旨在接受发票的集合,但是我们将需要单独处理每个发票。 更具体地说,具有“收集”类型有效负载的消息将被拆分为多个消息,其中每个消息将具有单独的发票作为有效负载。
- 筛选器 -我们的系统旨在仅自动处理开具少于$ 10,000的那些发票
- 路由器 -有些发票使用IBAN帐号,我们有两个不同的帐户-一个用于本地交易,一个用于国外交易。 路由器组件的工作是将带有发票的消息发送到正确的通道-无论是本地发票还是国外发票。
- 变形金刚 –当我们在系统中接受发票时,我们的银行API可以与其他类型一起使用–付款。 转换器组件的工作是根据提供的逻辑获取一些消息并将其转换为另一条消息。 我们希望将原始消息(发票)的有效负载转换为新的有效负载-付款。
- Banking Service Activator –处理发票并生成一些实际付款后,我们准备与外部银行系统进行对话。 我们已经公开了此类系统的服务,当携带付款的消息进入正确的(银行)渠道时,我们想激活一些逻辑–将付款传递给银行,然后让银行进行进一步处理。
创建项目
到目前为止,您应该对系统的功能以及其结构有一个较高的概述。 在开始编码之前,您将需要一个实际的Maven项目,并设置结构和所需的依赖关系。 如果您熟悉Maven,请参见下面的pom.xml文件,否则,如果您想节省一些时间,欢迎使用我为您创建的项目模板: 下载Maven项目模板 。
<?xml version='1.0' encoding='UTF-8'?>
<project xmlns='http://maven.apache.org/POM/4.0.0'xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd'><modelVersion>4.0.0</modelVersion><groupId>spring-integration-invoices</groupId><artifactId>spring-integration-invoices</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>3.2.1.RELEASE</version></dependency><dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-core</artifactId><version>2.2.1.RELEASE</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.16</version></dependency><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>13.0.1</version></dependency><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>6.5.2</version></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.7</source><target>1.7</target></configuration></plugin></plugins></build></project>
现在,让我们更详细地介绍系统的六个主要组件,并获得实际的代码。
1.发票网关
首先,让我们看一下Invoice的代码-这将是系统中的核心类之一。 我将使用com.vrtoonjava软件包作为根软件包,使用发票和银行业务作为子软件包:
package com.vrtoonjava.invoices;import com.google.common.base.Objects;import java.math.BigDecimal;public class Invoice {private final String iban;private final String address;private final String account;private final BigDecimal dollars;public Invoice(String iban, String address, String account, BigDecimal dollars) {this.iban = iban;this.address = address;this.account = account;this.dollars = dollars;}public boolean isForeign() {return null != iban && !iban.isEmpty();}public String getAddress() {return address;}public String getAccount() {return account;}public BigDecimal getDollars() {return dollars;}public String getIban() {return iban;}@Overridepublic String toString() {return Objects.toStringHelper(this).add('iban', iban).add('address', address).add('account', account).add('dollars', dollars).toString();}}
想象一下,我们从另一个系统(数据库,Web服务或其他系统)获取发票,但是我们不想将此部分耦合到集成层。 我们将为此使用网关组件。 Gateway引入了一个协议 ,该协议将客户端代码与集成层分离(在我们的案例中为Spring Integration依赖项)。 让我们看一下InvoiceCollectorGateway的代码:
package com.vrtoonjava.invoices;import java.util.Collection;/*** Defines a contract that decouples client from the Spring Integration framework.*/
public interface InvoiceCollectorGateway {void collectInvoices(Collection<Invoice> invoices);}
现在,要实际使用Spring Integration,我们需要创建一个标准的Spring配置文件并使用Spring Integration名称空间。 首先,这是invoices-int-schema.xml文件。 将其放入src / main / resources 。 请注意,我们已经定义了一个logging-channel-adapter ,这是一个特殊的通道,我们将从记录器发送消息。 我们还使用窃听 –您可以将其视为一种全局拦截器,它将向日志记录器通道发送与日志记录相关的消息。
<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns = 'http://www.springframework.org/schema/beans'xmlns:xsi = 'http://www.w3.org/2001/XMLSchema-instance'xmlns:int = 'http://www.springframework.org/schema/integration'xsi:schemaLocation = 'http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd'><!-- intercept and log every message --><int:logging-channel-adapter id='logger' level='DEBUG' /><int:wire-tap channel = 'logger' />
</beans>
现在回到我们的网关。 我们已经定义了一个网关接口–这是客户端将使用的依赖项。 当客户端调用collectInvoices方法时,网关将向newInvoicesChannel通道发送一条新消息(包含List负载)。 这使客户端与消息传递工具脱钩,但是让我们将结果放置到实际的消息传递通道中。 要配置网关,请将以下代码添加到集成模式配置中:
<int:channel id = 'newInvoicesChannel' /><int:gateway id='invoicesGateway'service-interface='com.vrtoonjava.invoices.InvoiceCollectorGateway'><int:method name='collectInvoices' request-channel='newInvoicesChannel' />
</int:gateway>
2.发票分割器
从网关,我们正在向包含发票集合的系统发送一条大消息,换句话说,消息具有“收集”类型的有效负载。 当我们要单独处理发票时,我们将从newInvoicesChannel获得结果并使用分离器组件,该组件将创建多条消息。 这些新消息中的每一个将具有发票类型的有效负载。 然后,我们将消息放置到新渠道– singleInvoicesChannel 。 我们将使用Spring Integration提供的默认拆分器(默认情况下,Spring Integration使用DefaultMessageSplitter来实现我们想要的功能)。 这是我们定义分离器的方式:
<int:splitterinput-channel='newInvoicesChannel'output-channel='singleInvoicesChannel' /><int:channel id = 'singleInvoicesChannel' />
3.过滤一些发票
我们系统的业务用例要求我们仅自动处理发出少于$ 10,000的发票。 为此,我们将介绍一个过滤器组件。 我们将从singleInvoicesChannel抓取消息,对它们应用过滤逻辑,然后将匹配的结果写入新的filterInvoicesChannel通道。 首先,让我们创建一个标准的Java类,其中将包含针对单个发票的过滤逻辑。 请注意,我们使用@Component注释(这使其成为标准的Spring bean),并使用@Filter注释注释过滤方法-这将告诉Spring Integration使用此方法过滤逻辑:
package com.vrtoonjava.invoices;import org.springframework.integration.annotation.Filter;
import org.springframework.stereotype.Component;@Component
public class InvoiceFilter {public static final int LOW_ENOUGH_THRESHOLD = 10_000;@Filterpublic boolean accept(Invoice invoice) {boolean lowEnough = invoice.getDollars().intValue() < LOW_ENOUGH_THRESHOLD;System.out.println('Amount of $' + invoice.getDollars()+ (lowEnough ? ' can' : ' can not') + ' be automatically processed by system');return lowEnough;}}
请注意,这是一个标准的POJO,我们可以轻松对其进行单元测试 ! 就像我之前说过的那样,Spring Integration并未将我们与其消息传递工具紧密耦合。 为了简洁起见,我不在本教程中粘贴单元测试–但是,如果您有兴趣, 请继续下载github项目并亲自查看测试 。
让我们为消息传递层指定输入/输出通道,并将过滤器挂入。将以下代码添加到集成模式配置中:
<int:filterinput-channel='singleInvoicesChannel'output-channel='filteredInvoicesChannel'ref='invoiceFilter' /><int:channel id = 'filteredInvoicesChannel' />
4.路由发票
到目前为止,我们已经拆分并过滤了一些发票。 现在是时候更仔细地检查每个发票的内容并决定是从当前国家(本地)还是从另一个国家(外国)发行的发票了。 为此,我们可以像以前一样处理并将自定义类用于路由逻辑。 我们(出于演示目的)现在将采用另一种方法-我们将使用Spring Expression Language(SpEL)来完全声明性地使用和处理路由。 还记得发票类上的isForeign方法吗? 我们可以在路由器声明中使用SpEL直接调用它(通过使用selector-expression属性)! 路由器将查看有效负载,评估它是国外发票还是本地发票,并将其转发到相应的渠道:
<int:recipient-list-router input-channel='filteredInvoicesChannel'><int:recipient channel = 'foreignTransactions' selector-expression='payload.foreign' /><int:recipient channel = 'localTransactions' selector-expression='!payload.foreign' />
</int:recipient-list-router><int:channel id = 'foreignTransactions' />
<int:channel id = 'localTransactions' />
我们将在本教程的第二部分中继续开发此应用程序。
参考: Spring Integration –从头开始的应用程序,来自 vrtoonjava博客的JCG合作伙伴 Michal Vrtiak的第1部分 。
翻译自: https://www.javacodegeeks.com/2013/03/spring-integration-application-from-scratch-part-1.html