Apache Camel –从头开始开发应用程序(第2部分/第2部分)
這是本教程的第二部分,我們將使用Apache Camel創建發票處理應用程序。 如果您錯過了它,一定要看一下第一部分 。 以前,我們已經定義了系統的功能要求,創建了網關,分離器,過濾器和基于內容的路由器組件。 讓我們繼續創建一個轉換器。
5.將發票轉換為付款
現在,我們已經成功地從系統中過濾掉了“過于昂貴”的發票(它們可能需要人工檢查等)。 重要的是,我們現在可以收取發票并從中產生付款。 首先,讓我們將Payment類添加到banking包中:
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放入banking包:
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);}}現在,我們可以將兩個實現添加到invoices包中了。 首先,讓我們創建LocalPaymentCreator類:
package com.vrtoonjava.invoices;import com.vrtoonjava.banking.Payment; import com.vrtoonjava.banking.PaymentCreator; import com.vrtoonjava.banking.PaymentException; 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";@Overridepublic 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.stereotype.Component;@Component public class ForeignPaymentCreator implements PaymentCreator {// hard coded account value for demo purposesprivate static final String CURRENT_IBAN_ACC = "current-iban-acc";@Overridepublic 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());}}這兩個創建者是簡單的Spring bean,Apache Camel提供了一種將它們連接到路由的非常好的方法。 我們將在Camel的Java DSL上使用transform()方法創建兩個轉換器。 我們將正確的轉換器插入seda:foreignInvoicesChannel seda:localInvoicesChannel和seda:foreignInvoicesChannel seda:localInvoicesChannel ,并使它們將結果轉發到seda:bankingChannel 。 將以下代碼添加到您的configure方法中:
from("seda:foreignInvoicesChannel").transform().method("foreignPaymentCreator", "createPayment").to("seda:bankingChannel");from("seda:localInvoicesChannel").transform().method("localPaymentCreator", "createPayment").to("seda:bankingChannel");6.將付款轉到銀行服務(服務激活器)
付款準備就緒,包含付款的消息正在seda:bankingChannel中等待。 該流程的最后一步是使用Service Activator組件。 它的工作方式很簡單–當頻道中出現新消息時,Apache Camel會調用Service Activator組件中指定的邏輯。 換句話說,我們正在將外部服務連接到我們現有的消息傳遞基礎結構。
為此,我們首先需要查看銀行服務合同。 因此,將BankingService接口BankingService到banking程序包中(在現實世界中,它可能駐留在某些外部模塊中):
現在,我們將需要BankingService的實際實現。 同樣,實現不太可能駐留在我們的項目中(它可能是遠程公開的服務),但是至少出于教程目的,讓我們創建一些模擬實現。 將MockBankingService類添加到banking包:
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類添加到invoices包中:
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.stereotype.Component;/*** Endpoint that picks Payments from the system and dispatches them to the* service provided by bank.*/ @Component public class PaymentProcessor {@AutowiredBankingService bankingService;public void processPayment(Payment payment) throws PaymentException {bankingService.pay(payment);}}Apache Camel提供了一種簡單的方法,當消息到達特定端點時,如何在任意bean上調用方法( EIP將其描述為服務激活器),方法是在Camel的Java DSL上使用bean()方法:
from("seda:bankingChannel").bean(PaymentProcessor.class, "processPayment");錯誤處理
消息傳遞系統的最大挑戰之一是正確識別和處理錯誤情況。 EAI描述了許多方法,我們將使用Camel的Dead Letter Channel EIP的實現。 死信通道只是另一個通道,當該通道中出現錯誤消息時,我們可以采取適當的措施。 在實際的應用程序中,我們可能會尋求一些重試邏輯或專業報告,在我們的示例教程中,我們只會打印出錯誤原因。 讓我們修改先前定義的Service Activator并插入errorHandler()組件。 當PaymentProcessor引發異常時,此errorHandler會將引起錯誤的原始消息轉發到Dead Letter Channel:
from("seda:bankingChannel").errorHandler(deadLetterChannel("log:failedPayments")).bean(PaymentProcessor.class, "processPayment");最后,這是最終的完整路線:
package com.vrtoonjava.routes;import com.vrtoonjava.invoices.LowEnoughAmountPredicate; import com.vrtoonjava.invoices.PaymentProcessor; import org.apache.camel.LoggingLevel; import org.apache.camel.builder.RouteBuilder; import org.springframework.stereotype.Component;@Component public class InvoicesRouteBuilder extends RouteBuilder {@Overridepublic void configure() throws Exception {from("seda:newInvoicesChannel").log(LoggingLevel.INFO, "Invoices processing STARTED").split(body()).to("seda:singleInvoicesChannel");from("seda:singleInvoicesChannel").filter(new LowEnoughAmountPredicate()).to("seda:filteredInvoicesChannel");from("seda:filteredInvoicesChannel").choice().when().simple("${body.isForeign}").to("seda:foreignInvoicesChannel").otherwise().to("seda:localInvoicesChannel");from("seda:foreignInvoicesChannel").transform().method("foreignPaymentCreator", "createPayment").to("seda:bankingChannel");from("seda:localInvoicesChannel").transform().method("localPaymentCreator", "createPayment").to("seda:bankingChannel");from("seda:bankingChannel").errorHandler(deadLetterChannel("log:failedPayments")).bean(PaymentProcessor.class, "processPayment");}}運行整個事情
現在,我們將創建一個作業(它將以固定的速率)將新發票發送到系統。 它只是利用Spring的@Scheduled注釋的標準Spring bean。 因此,我們向項目添加一個新類– InvoicesJob :
package com.vrtoonjava.invoices;import org.apache.camel.Produce; 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;@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 = "camel-config.xml" /><context:component-scan base-package = "com.vrtoonjava" /><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 13:48:54.347 INFO [Camel (camel-1) thread #0 - seda://newInvoicesChannel][route1] Invoices processing STARTED Amount of $4201 can be automatically processed by system Amount of $15110 can not be automatically processed by system Amount of $17165 can not be automatically processed by system Amount of $1193 can be automatically processed by system Amount of $6077 can be automatically processed by system Amount of $17164 can not be automatically processed by system Amount of $11272 can not be automatically processed by system Processing payment Payment{senderAccount=current-local-acc, receiverAccount=test-account 1901000, dollars=4201} Amount of $3598 can be automatically processed by system Amount of $14449 can not be automatically processed by system Processing payment Payment{senderAccount=current-local-acc, receiverAccount=test-account 8911000, dollars=1193} Amount of $12486 can not be automatically processed by system 13:48:54.365 INFO [Camel (camel-1) thread #5 - seda://bankingChannel][failedPayments] Exchange[ExchangePattern: InOnly, BodyType: com.vrtoonjava.banking.Payment, Body: Payment{senderAccount=current-iban-acc, receiverAccount=test-iban-7451000, dollars=6077}] Processing payment Payment{senderAccount=current-iban-acc, receiverAccount=test-iban-6201000, dollars=3598} 參考: Apache Camel –在vrtoonjava博客上從JCG合作伙伴 Michal Vrtiak 從頭開始開發應用程序(第2部分/第2部分) 。翻譯自: https://www.javacodegeeks.com/2013/11/apache-camel-developing-application-from-the-scratch-part-2-2.html
總結
以上是生活随笔為你收集整理的Apache Camel –从头开始开发应用程序(第2部分/第2部分)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 房子备案了没房产证怎么办(房子备案了没房
- 下一篇: pk10官方下载(pk10安卓)