这是本教程的第二部分,我们将使用Spring Integration创建发票处理应用程序。 如果您错过了它,一定要看一下第一部分 。 以前,我们已经定义了系统的功能要求,创建了网关,分离器,过滤器和路由器组件。 让我们继续创建一个转换器。

5.将发票转换为付款

现在,我们已经成功地从系统中过滤掉了“过于昂贵”的发票(它们可能需要人工检查等)。 重要的是,我们现在可以收取发票并从中产生付款。 首先,让我们将Payment类添加到银行
包:

package com.vrtoonjava.banking;import com.google.common.base.Objects;import java.math.BigDecimal;public class Payment {private final String senderAccount;private final String receiverAccount;private final BigDecimal dollars;public Payment(String senderAccount, String receiverAccount, BigDecimal dollars) {this.senderAccount = senderAccount;this.receiverAccount = receiverAccount;this.dollars = dollars;}public String getSenderAccount() {return senderAccount;}public String getReceiverAccount() {return receiverAccount;}public BigDecimal getDollars() {return dollars;}@Overridepublic String toString() {return Objects.toStringHelper(this).add('senderAccount', senderAccount).add('receiverAccount', receiverAccount).add('dollars', dollars).toString();}}

因为我们将有两种方法(从本地和国外发票)创建付款,所以我们定义一个用于创建付款的通用合同(界面)。 将界面PaymentCreator放入银行业务包:

package com.vrtoonjava.banking;import com.vrtoonjava.invoices.Invoice;/*** Creates payment for bank from the invoice.* Real world implementation might do some I/O expensive stuff.*/
public interface PaymentCreator {Payment createPayment(Invoice invoice) throws PaymentException;}

从技术上讲,这是一个简单的参数化工厂。 请注意,它将引发PaymentException 。 稍后我们将进行异常处理,但这是简单的PaymentException的代码:

package com.vrtoonjava.banking;public class PaymentException extends Exception {public PaymentException(String message) {super(message);}}

现在,我们可以将两个实现添加到发票包中了。 首先,让我们创建LocalPaymentCreator类:

package com.vrtoonjava.invoices;import com.vrtoonjava.banking.Payment;
import com.vrtoonjava.banking.PaymentCreator;
import com.vrtoonjava.banking.PaymentException;
import org.springframework.integration.annotation.Transformer;
import org.springframework.stereotype.Component;@Component
public class LocalPaymentCreator implements PaymentCreator {// hard coded account value for demo purposesprivate static final String CURRENT_LOCAL_ACC = 'current-local-acc';@Override@Transformerpublic Payment createPayment(Invoice invoice) throws PaymentException {if (null == invoice.getAccount()) {throw new PaymentException('Account can not be empty when creating local payment!');}return new Payment(CURRENT_LOCAL_ACC, invoice.getAccount(), invoice.getDollars());}}

另一个创建者将是ForeignPaymentCreator ,它具有相当简单的实现:

package com.vrtoonjava.invoices;import com.vrtoonjava.banking.Payment;
import com.vrtoonjava.banking.PaymentCreator;
import com.vrtoonjava.banking.PaymentException;
import org.springframework.integration.annotation.Transformer;
import org.springframework.stereotype.Component;@Component
public class ForeignPaymentCreator implements PaymentCreator {// hard coded account value for demo purposesprivate static final String CURRENT_IBAN_ACC = 'current-iban-acc';@Override@Transformerpublic Payment createPayment(Invoice invoice) throws PaymentException {if (null == invoice.getIban()) {throw new PaymentException('IBAN mustn't be null when creating foreign payment!');}return new Payment(CURRENT_IBAN_ACC, invoice.getIban(), invoice.getDollars());}}

有关创建者的有趣部分是@Transformer批注。 这是与@Filter注释一起使用的概念类似–只是这次我们告诉Spring Integration它应该使用此方法进行有效负载转换逻辑。 无论哪种方式,我们都将使用外部或本地转换器,因此新消息将在BankingChannel通道中结束。 让我们在架构文件中定义这些新的转换器:

<int:transformerinput-channel='localTransactions'output-channel='bankingChannel'ref='localPaymentCreator' /><int:transformerinput-channel='foreignTransactions'output-channel='bankingChannel'ref='foreignPaymentCreator' /><int:channel id = 'bankingChannel'><int:queue capacity='1000' />
</int:channel>

6.将付款转到银行服务(服务激活器)

付款已准备就绪,包含付款的消息正在bankingChannel中等待。 该流程的最后一步是使用Service Activator组件。 它的工作方式很简单-当新消息出现在通道中时,Spring Integration会调用Service Activator组件中指定的逻辑。 因此,当新的付款出现在bankingChannel中时,我们希望将其传递给银行服务。
为此,我们首先需要查看银行服务合同。 因此,将BankingService接口添加银行程序包中(在现实世界中,它可能驻留在某些外部模块中):

package com.vrtoonjava.banking;/*** Contract for communication with bank.*/
public interface BankingService {void pay(Payment payment) throws PaymentException;}

现在,我们将需要BankingService的实际实现。 同样,实现不太可能驻留在我们的项目中(它可能是远程公开的服务),但是至少出于教程目的,让我们创建一些模拟实现。 将MockBankingService类添加到银行业务包:

package com.vrtoonjava.banking;import org.springframework.stereotype.Service;import java.util.Random;/*** Mock service that simulates some banking behavior.* In real world, we might use some web service or a proxy of real service.*/
@Service
public class MockBankingService implements BankingService {private final Random rand = new Random();@Overridepublic void pay(Payment payment) throws PaymentException {if (rand.nextDouble() > 0.9) {throw new PaymentException('Banking services are offline, try again later!');}System.out.println('Processing payment ' + payment);}}

模拟实施会在某些随机情况下(约10%)造成失败。 当然,为了实现更好的解耦,我们不会直接使用它,而是将根据自定义组件在合同(接口)上创建依赖关系。 让我们现在将PaymentProcessor类添加到发票包中:

package com.vrtoonjava.invoices;import com.vrtoonjava.banking.BankingService;
import com.vrtoonjava.banking.Payment;
import com.vrtoonjava.banking.PaymentException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.stereotype.Component;/*** Endpoint that picks Payments from the system and dispatches them to the* service provided by bank.*/
@Component
public class PaymentProcessor {@AutowiredBankingService bankingService;@ServiceActivatorpublic void processPayment(Payment payment) throws PaymentException {bankingService.pay(payment);}}

再次-注意@ServiceActivator批注。 这意味着当服务激活器组件出现在游戏中时,Spring Integration应该调用相应的方法。 要使用服务激活器,我们需要将其添加到集成模式中:

<int:service-activator input-channel='bankingChannel' ref='paymentProcessor'><int:poller fixed-rate='500' error-channel='failedPaymentsChannel' />
</int:service-activator><int:channel id = 'failedPaymentsChannel' />

请注意,我们正在定义固定速率属性,这意味着将每半秒调用一次激活器(如果BankingChannel中存在某些消息)。 我们还定义了错误通道属性,但是我们很快就会到达那里。

错误处理

消息传递系统的最大挑战之一是正确识别和处理错误情况。 Spring Integration提供了一种称为“错误通道”的技术,我们可以(显然)从系统发送错误消息。 错误通道只是另一个通道,当此通道中出现错误消息时,我们可以采取适当的措施。 在实际的应用程序中,我们可能会寻求一些重试逻辑或专业报告,在我们的示例教程中,我们只会打印出错误原因。 在上一个组件(服务激活器)中,我们指定了error-channel属性来引用failedPaymentsChannel 。 当消息到达此通道时,我们将调用另一个服务激活器并打印出错误。 这是FailedPaymentHandler服务激活器的实现:

package com.vrtoonjava.invoices;import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.stereotype.Component;@Component
public class FailedPaymentHandler {@ServiceActivatorpublic void handleFailedPayment(Exception e) {System.out.println('Payment failed: ' + e);// now the system should do something reasonable, like retrying the payment// omitted for the tutorial purposes}}

然后像往常一样将其连接到集成模式:

<int:service-activatorinput-channel='failedPaymentsChannel'ref='failedPaymentHandler' />

运行整个事情

现在,我们将创建一个作业(它将以固定的速率)将新发票发送到系统。 它只是利用Spring的@Scheduled注释的标准Spring bean。 因此,我们向项目添加一个新类– InvoicesJob

package com.vrtoonjava.invoices;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.util.ArrayList;
import java.util.Collection;
import java.util.List;/*** Job that every n-seconds generates invoices and sends them to the system.* In real world this might be endpoint receiving invoices from another system.*/
@Component
public class InvoicesJob {private int limit = 10; // default value, configurable@AutowiredInvoiceCollectorGateway invoiceCollector;@AutowiredInvoiceGenerator invoiceGenerator;@Scheduled(fixedRate = 4000)public void scheduleInvoicesHandling() {Collection<Invoice> invoices = generateInvoices(limit);System.out.println('\n===========> Sending ' + invoices.size() + ' invoices to the system');invoiceCollector.collectInvoices(invoices);}// configurable from Injectorpublic void setLimit(int limit) {this.limit = limit;}private Collection<Invoice> generateInvoices(int limit) {List<Invoice> invoices = new ArrayList<>();for (int i = 0; i < limit; i++) {invoices.add(invoiceGenerator.nextInvoice());}return invoices;}}

Job会调用(每4秒一次) InvoicesGenerator并将发票转发到Gateway(我们了解的第一个组件)。 为了使其工作,我们还需要InvoicesGenerator类:

package com.vrtoonjava.invoices;import org.springframework.stereotype.Component;import java.math.BigDecimal;
import java.util.Random;/*** Utility class for generating invoices.*/
@Component
public class InvoiceGenerator {private Random rand = new Random();public Invoice nextInvoice() {return new Invoice(rand.nextBoolean() ? iban() : null, address(), account(), dollars());}private BigDecimal dollars() {return new BigDecimal(1 + rand.nextInt(20_000));}private String account() {return 'test-account ' + rand.nextInt(1000) + 1000;}private String address() {return 'Test Street ' + rand.nextInt(100) + 1;}private String iban() {return 'test-iban-' + rand.nextInt(1000) + 1000;}}

这只是一个简单的模拟功能,可让我们看到系统的运行情况。 在现实世界中,我们不会使用任何生成器,而可能会使用某些公开的服务。 现在,在resources文件夹下创建一个新的spring配置文件– invoices-context.xml并声明组件扫描和任务计划支持:

<?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:task = 'http://www.springframework.org/schema/task'xmlns:context = 'http://www.springframework.org/schema/context'xsi:schemaLocation = 'http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd'><import resource = 'invoices-int-schema.xml' /><context:component-scan base-package = 'com.vrtoonjava.invoices' /><context:component-scan base-package = 'com.vrtoonjava.banking' /><task:executor id = 'executor' pool-size='10' /><task:scheduler id = 'scheduler' pool-size='10' /><task:annotation-driven executor='executor' scheduler='scheduler' /></beans>

要查看整个运行过程,我们还需要最后一块-标准Java主应用程序,我们将在其中创建Spring的ApplicationContext。

package com.vrtoonjava.invoices;import org.springframework.context.support.ClassPathXmlApplicationContext;/*** Entry point of the application.* Creates Spring context, lets Spring to schedule job and use schema.*/
public class InvoicesApplication {public static void main(String[] args) {new ClassPathXmlApplicationContext('/invoices-context.xml');}}

只需从命令行运行mvn clean install并在InvoicesApplication类中启动main方法。 您应该能够看到类似的输出:

===========> Sending 10 invoices to the system
Amount of $3441 can be automatically processed by system
Amount of $17419 can not be automatically processed by system
Processing payment Payment{senderAccount=current-local-acc, receiverAccount=test-account 1011000, dollars=3441}
Amount of $18442 can not be automatically processed by system
Amount of $19572 can not be automatically processed by system
Amount of $5471 can be automatically processed by system
Amount of $1663 can be automatically processed by system
Processing payment Payment{senderAccount=current-iban-acc, receiverAccount=test-iban-2211000, dollars=5471}
Amount of $13160 can not be automatically processed by system
Amount of $2213 can be automatically processed by system
Amount of $1423 can be automatically processed by system
Processing payment Payment{senderAccount=current-iban-acc, receiverAccount=test-iban-8051000, dollars=1663}
Amount of $1267 can be automatically processed by system
Payment failed: org.springframework.integration.MessageHandlingException: com.vrtoonjava.banking.PaymentException: Banking services are offline, try again later!
Processing payment Payment{senderAccount=current-iban-acc, receiverAccount=test-iban-6141000, dollars=1423}
Processing payment Payment{senderAccount=current-local-acc, receiverAccount=test-account 6761000, dollars=1267}

参考: Spring Integration –从头开始的应用程序,来自我们的JCG合作伙伴 Michal Vrtiak的第2部分 ,在vrtoonjava博客上。

翻译自: https://www.javacodegeeks.com/2013/03/spring-integration-application-from-scratch-part-2.html

Spring集成–从头开始应用程序,第2部分相关推荐

  1. Spring集成–从头开始应用程序,第1部分

    开始之前 在本教程中,您将学习什么是Spring Integration ,如何使用它以及有助于解决哪些问题. 我们将从头开始构建一个示例应用程序,并演示Spring Integration的一些核心 ...

  2. spring集成jndi_Spring应用程序与JNDI连接池的集成测试

    spring集成jndi 我们都知道,无论何时连接到数据库,都需要使用连接池. 所有使用JDBC 4类的现代驱动程序都支持它. 在本文中,我们将概述Spring应用程序中的连接池,以及如何在非JEE环 ...

  3. 将Spring集成到旧版应用程序中

    所有Spring开发人员喜欢做的事情之一就是将Spring塞入他们正在工作的任何应用程序中–这是我生活中的罪恶感之一:您看到一些代码,认为它是垃圾,因为它包含几个众所周知的反模式,然后想想如果这个应用 ...

  4. apache camel_Apache Camel –从头开始开发应用程序(第1部分/第2部分)

    apache camel 开始之前 前段时间,我写了一篇关于Spring Integration的教程,以演示如何在受现实发票处理系统启发的示例应用程序中使用Spring Integration. 我 ...

  5. Spring集成和Web服务

    本文是我们名为" Spring Integration for EAI "的学院课程的一部分. 在本课程中,向您介绍了企业应用程序集成模式以及Spring Integration如 ...

  6. gwt格式_使用Spring Security保护GWT应用程序的安全

    gwt格式 在本教程中,我们将看到如何将GWT与Spring的安全模块(即Spring Security)集成. 我们将看到如何保护GWT入口点,如何检索用户的凭据以及如何记录各种身份验证事件. 此外 ...

  7. Apache Camel –从头开始开发应用程序(第1部分/第2部分)

    开始之前 前一段时间,我写了一篇关于Spring Integration的教程,以演示如何在受现实发票处理系统启发的示例应用程序中使用Spring Integration. 我对此非常满意,因此我决定 ...

  8. 使用Spring Security保护GWT应用程序

    在本教程中,我们将看到如何将GWT与Spring的安全模块(即Spring Security)集成在一起. 我们将看到如何保护GWT入口点,如何检索用户的凭据以及如何记录各种身份验证事件. 此外,我们 ...

  9. spring集成kafka,以及常见错误解决

    spring集成kafka,以及常见错误解决 一.配置kafka 1.引入jar包 <!--Kafka和spring集成的支持类库,spring和kafka通信监听--><!-- h ...

最新文章

  1. MyEclipse-Web开发时何时重启tomcat、重新部署
  2. MATLAB图像处理-特征提取-形状特征 方法小结
  3. openCV学习教程(一):Mat类的使用
  4. android studio开关按钮,Android studio实现滑动开关
  5. SAP Spartacus checkout 流程使用 url 粘贴直接跳转到 delivery mode不能打开页面的原因
  6. Booting ARM Linux SMP on MPCore
  7. 发布composer包到 Packagist,并设置自动同步(从github到Packagist)
  8. java 判断值是否设置,获取Java中“-非法访问”设置的当前值
  9. 深度学习head、neck、backbone三个术语分别是指什么?
  10. Linux中的ls命令详细使用
  11. html怎么写海贼王旗帜图片,海贼王精美旗帜套图
  12. MySQL代码错误号大全
  13. Rockchip基于RK3566/RK3568 WiFi AP6256调试笔记
  14. phpini修改时间无效_php配置文件改变为什么没有生效?
  15. ueditor 工具栏配置_Ueditor文本编辑工具栏自定义
  16. Yii2日志使用方法
  17. css投影效果的兼容代码
  18. 新手学美发的最基本知识
  19. 从0到1实现单机记账APP原理与细节uniApp内含源码 (一)
  20. 在menuconfig中选择m和 y的区别

热门文章

  1. 2015c语言9月答案,2015年9月计算机二级C语言预测题答案
  2. 体质测试数据统计软件,[体质测试数据excel自动统计表]体质测试数据Excel自动统计模板的研制...
  3. 关于Object.clone克隆方法的测试
  4. leetcode初级算法3.存在重复元素
  5. wxpay-api:pay_J2Pay – API响应
  6. angluar cdk_零分钟即可在容器开发套件(CDK)上实现云运营
  7. cloudwatch_将CloudWatch Logs与Cloudhub Mule集成
  8. apache.camel_Apache Camel 2.18发布–包含内容
  9. apache spark_Apache Spark:更改架构之前必须解决的5个陷阱
  10. Kogito人机规则-第1部分:进一步解决流口水