在这篇文章中,我们将看看如何将Apache Camel与Spring Boot***集成。
Apache Camel是一个集成框架,目的是把不同的系统放在一起,使其稳健地工作。在企业系统中,总是有工作要连接不同的系统。Apache Camel为开发者提供了一种方法,让他们专注于自己的业务逻辑,而不需要将你的数据转换为规范的格式。Camel通过支持80多个协议和数据类型的API实现。因此,作为一个开发者,你只需要知道Camel是如何把所有东西粘在一起的。在这篇文章中,我们将看看将Apache Camel与Spring Boot整合的步骤。
在展示一个使用Spring Boot的例子之前,最好先了解一下Camel的核心概念和术语。
系统用于相互通信的实体。
交易所封装消息并提供系统间的交互。它是决定消息类型的消息容器。
Camel Context是Camel的核心模型,提供对Routes、Endpoints等服务的访问。
一种抽象,允许客户和服务器独立工作。我们用特定领域语言创建路由,它们是一连串的函数调用(处理器)。
处理器和端点通过使用DSL将它们连在一起,最后形成路由。在我们的例子中,DSL是JAVA流畅的API,但如果我们将Camel与其他语言/框架一起使用,那么DSL也可以是XML等。
处理器执行交换操作。我们可以认为路由是一个逻辑单元,它连接正确的处理器来处理一个消息。
组件是Apache Camel的扩展单元。它们是使Camel非常容易与其他系统集成的单元。请看core components支持的组件的完整列表。组件作为Endpoints的工厂,通过给定的URI创建它们。
端点是服务的连接点,将系统与其他系统连接起来。我们通过给定URI的组件来创建端点。例如,为了创建一个FTP连接,在路由中提供以下URI:<em>ftp://[[email protected]]hostname[:port]/directoryname[?options]</em>
,组件以给定的配置创建一个FTP的端点。
生产者是Camel的单元,它创建并发送消息到一个端点。
消费者是Camel的单元,它接收由生产者创建的消息,将其包装成交换物并将其发送给处理器。
到目前为止,我们总结了Camel的主要部分。没有必要去了解每个概念的细节,但有一个Camel的架构概述是很好的,这有助于我们正确使用它。在我们下面的例子中,我们将展示它们是如何被集成到Spring Boot中的。
我们将创建一个应用程序。
为此,我们将使用H2、Spring Web、Spring JPA和Apache Camel。
用以下依赖项创建你的maven项目。你可以使用你的IDE或Spring Initializr来启动你的应用程序。以下是完整的pom.xml及其解释。
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--Get required dependencies from a parent-->
<parent>
<groupId>org.apache.camel</groupId>
<artifactId>camel-dependencies</artifactId>
<version>3.3.0</version>
</parent>
<artifactId>spring-boot-camel</artifactId>
<name>spring-boot-camel</name>
<description>Spring Boot Camel integration tutorial</description>
<properties>
<spring-boot-version>2.2.7.RELEASE</spring-boot-version>
<run.profiles>dev</run.profiles>
</properties>
<dependencyManagement>
<dependencies>
<!--Import as a pom to let spring-boot to manage spring-boot dependencies version -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot-version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--Import as a pom to let camel manage camel-spring-boot dependencies version-->
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-dependencies</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!--Spring boot dependencies to enable REST, JPA and Core features-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--Camel Spring Boot Dependencies to enable REST, JSON, SWAGGER, JPA features-->
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-servlet-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jackson-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-swagger-java-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jpa-starter</artifactId>
</dependency>
<!--In memory database-->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!--Spring boot testing-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot-version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
在处理Apache Camel之前,我们需要设置一些实体、服务和存储库。
创建一个带有id、name、price和discounted字段的Product实体。我们也要创建一个命名查询,它可以从Camel中调用其名称并返回查询结果。我们的discounted-products命名查询返回所有有折扣的产品。
@Entity
@Table(name = "products")
@NamedQuery(name = "discounted-products", query = "select product from Product product where product.discounted IS NOT NULL")
public class Product {
@Id
@GeneratedValue
private int id;
private String name;
private Integer price;
private Integer discounted;
// Getters and setters
}
创建一个ProductRepository类,它扩展自Spring Data的CrudRepository
。这个扩展为我们提供了随时可以调用的查询,如findAll, findById, save等。
public interface ProductRepository extends CrudRepository<Product, Integer> {
}
创建一个ProductService
类,并用服务注解对其进行注解。使用构造函数注入来从Spring Context中获取ProductRepository。我们提供基本的findById<code>, <code>findAll
,并保存不言而喻的功能。
@Service
public class ProductService {
private final ProductRepository products;
@Autowired
public ProductService(ProductRepository products) {
this.products = products;
}
public Product findById(Integer id) {
Optional < Product > product = products.findById(id);
if (!product.isPresent()) {
throw new IllegalStateException("Product could not found for given id:" + id);
}
return product.get();
}
public Iterable < Product > findAll() {
return products.findAll();
}
public void save(Product product) {
products.save(product);
}
}
作为最后一步,在src/main/resources
中创建一个data.sql
文件并插入3个产品,如下所示。Spring将在启动时自动运行data.sql
。阅读更多关于启动脚本的信息
INSERT INTO products (id, name, price, discounted)
VALUES
(1, 'Book', 25, NULL),
(2, 'Watch', 100, NULL),
(3, 'Shoes', 40, NULL);
创建带有id、金额和产品字段的折扣实体。一个产品在给定的时间内可能发生一次折扣,所以在产品字段上建立OneToOne关系。
@Entity
@Table(name = "discounts")
public class Discount {
@Id
@GeneratedValue
private int id;
private Integer amount;
@OneToOne
private Product product;
// Getters and setters
}
像我们一样创建DiscountRepository。
public interface DiscountRepository extends CrudRepository<Discount, Integer> {}
创建DiscountService类,类似于ProductService。除了findDiscount
方法和findProduct
的工作原理一样,我们还有makeDiscount
函数。这个函数生成一个随机折扣,从数据库中获取随机产品,并将折扣应用于该产品。
@Service
public class DiscountService {
private final DiscountRepository discounts;
private final ProductService productService;
private final Random random = new Random();
@Autowired
public DiscountService(DiscountRepository discounts,
ProductService productService) {
this.discounts = discounts;
this.productService = productService;
}
public Discount makeDiscount() {
// create a discount
Discount discount = new Discount();
int discountRate = this.random.nextInt(100);
discount.setAmount(discountRate);
// select random product
int productId = this.random.nextInt(3) + 1;
Product product = productService.findById(productId);
// set the discount to product and save
int discountedPrice = product.getPrice() - (discountRate * product.getPrice() / 100);
product.setDiscounted(discountedPrice);
productService.save(product);
discount.setProduct(product);
return discount;
}
public Discount findDiscount(Integer id) {
Optional < Discount > discount = discounts.findById(id);
if (!discount.isPresent()) {
throw new IllegalStateException("Discount could not found for given id:" + id);
}
return discount.get();
}
}
创建application-dev.yml
来配置contextPath
映射需要的Camel。添加自定义的折扣属性,这些属性将在我们的路线中使用。
camel:
component:
servlet:
mapping:
contextPath: /javadevjournal/*
discount:
newDiscountPeriod: 2000
listDiscountPeriod: 6000/pre>
到目前为止,我们在处理Apache Camel之前配置了我们的数据。现在让我们来使用它。
Camel提供RouteBuilder作为创建路由的基类。我们需要扩展它,并用@Component
来注释它。正如我们前面提到的,Apache Camel使用其上下文来引用对象。但是当与SpringBoot一起工作时,Camel首先搜索SpringBoot的上下文,然后将发现的对象注入到它的CamelContext
中,比如我们例子中的RouteBuilder
。
在创建了从RouteBuilder延伸出来的Routes类后,我们需要重写它的configure方法。我们想有一个逻辑,在某个给定的时期自动生成折扣。让我们先把下面的路由添加到我们的configure函数中并解释一下。
@Component
class TimedJobs extends RouteBuilder {
@Override
public void configure() {
from("timer:new-discount?delay=1000&period={{discount.newDiscountPeriod:2000}}")
.routeId("make-discount")
.bean("discountService", "makeDiscount")
.to("jpa:org.apache.camel.example.spring.boot.rest.jpa.Discount")
.log("Created %${body.amount} discount for ${body.product.name}");
// additional route will be added in the next step
}
在这里最好考虑一下我们的Camel术语,同时把它和Spring Boot一起使用。我们正在使用Java DSL创建路由。然后我们使用timerComponent,它是Camel提供的一个扩展。在引擎盖下,Camel通过我们最初的延迟和运行期配置,到达定时器端点来启动其生产者。
在进一步使用之前,值得一提的是,Apache Camel支持使用Spring Boot属性,正如我们在这里使用的那样。你可以直接用它的名字和默认值来引用它们,比如{{property_name:default_value}}.
然后定义make-discount路线,这应该是唯一的,以后可以参考。然后我们在discountService Bean中调用我们的makeDiscount函数。Message是Exchanged,可以用body前缀引用,Consumed由logger来记录。请参考Simple Language,了解你可以使用的表达式的完整列表。让我们也在前面的路由下面添加另一个路由,以列出所有的产品和它们的更新价格。
from("jpa:org.apache.camel.example.spring.boot.rest.jpa.Product"
+ "?namedQuery=discounted-products"
+ "&delay={{discount.listDiscountPeriod:6000}}"
+ "&consumeDelete=false")
.routeId("list-discounted-products")
.log(
"Discounted product ${body.name}. Price dropped from ${body.price} to ${body.discounted}");
我们正在为我们的产品实体使用JPA组件,并将其称为namedQuery
。consumeDelete
查询意味着我们不想删除处理过的产品实体,查看JPA Component的完整配置列表。下面是我们工作的日志。
Created %27 discount for Watch
Created %84 discount for Book
Created %92 discount for Shoes
Discounted product Book. Price dropped from 25 to 4
Discounted product Watch. Price dropped from 100 to 73
Discounted product Shoes. Price dropped from 40 to 4
到目前为止,我们配置了定时器组件来触发我们的功能。让我们也来整合REST端点并生成Swagger文档。创建一个新的路由,扩展RouteBuilder
,我们需要调用Camel的restConfiguration
函数来配置我们的应用程序。
@Component
class RestApi extends RouteBuilder {
@Override
public void configure() {
restConfiguration()
.contextPath("/javadevjournal")
.apiContextPath("/api-doc")
.apiProperty("api.title", "JAVA DEV JOURNAL REST API")
.apiProperty("api.version", "1.0")
.apiProperty("cors", "true")
.apiContextRouteId("doc-api")
.port(env.getProperty("server.port", "8080"))
.bindingMode(RestBindingMode.json);
rest("/products").description("Details of products")
.get("/").description("List of all products")
.route().routeId("products-api")
.bean(ProductService.class, "findAll")
.endRest()
.get("discounts/{id}").description("Discount of a product")
.route().routeId("discount-api")
.bean(DiscountService.class, "findDiscount(${header.id})");
}
}
我们将contextPath
设置为javadevjournal,API上下文路径设置为api-doc
,用于Swagger。绑定模式默认是关闭的。由于我们在pom.xml中添加了json-jackson,我们可以使用json绑定格式。在我们配置的第二部分,我们定义了/products
端点并返回Productservice
.findAll的结果。另外,我们用/discounts/{id}扩展/products
端点,并用从查询中获取的id调用Discountservice.findDiscount函数。{header}
指的是之前简单语言中提到的{body}
占位符传入的输入。
如果你访问http://localhost:8080/javadevjournal/api-doc
,你会得到Swagger响应。点击http://localhost:8080/javadevjournal/products
,你会得到。
[
{
"id": 1,
"name": "Book",
"price": 25,
"discounted": 4
},
{
"id": 2,
"name": "Watch",
"price": 100,
"discounted": 73
},
{
"id": 3,
"name": "Shoes",
"price": 40,
"discounted": 4
}
]
同样地,访问http://localhost:8080/javadevjournal/products/discounts/1
,你会得到
{
"id": 1,
"amount": 92,
"product": {
"id": 3,
"name": "Shoes",
"price": 40,
"discounted": 4
}
}
###总结
在这篇文章中,我们看到了如何将Apache Camel与Spring Boot整合在一起.我们简要介绍了什么是Apache Camel,如何利用实际场景将其与Spring Boot整合在一起。这个应用程序的源代码可以在Github上找到。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://www.javadevjournal.com/spring-boot/apache-camel-spring-boot/
内容来源于网络,如有侵权,请联系作者删除!