Apache Camel with Spring Boot – Apache Camel与Spring Boot

最后修改: 2017年 7月 24日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

At its core, Apache Camel is an integration engine, which – simply put – can be used to facilitate interactions between a wide and varied array of technologies.

在核心方面,Apache Camel是一个集成引擎,简单地说,它可以用来促进广泛和多样的技术之间的互动。

These bridges between services and technologies are called routes. Routes are implemented on an engine (the CamelContext), and they communicate with so-called “exchange messages”.

这些服务和技术之间的桥梁被称为routes。 Routes在一个引擎(CamelContext)上实现,它们与所谓的 “交换信息 “进行通信。

2. Maven Dependencies

2.Maven的依赖性

To start off, we’ll need to include dependencies for Spring Boot, Camel, Rest API with Swagger and JSON:

首先,我们需要包括Spring Boot、Camel、Rest API with Swagger和JSON的依赖。

<dependencies>
    <dependency>
        <groupId>org.apache.camel.springboot</groupId>
        <artifactId>camel-servlet-starter</artifactId>
        <version>3.15.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.camel.springboot</groupId>
        <artifactId>camel-jackson-starter</artifactId>
        <version>3.15.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.camel.springboot</groupId>
        <artifactId>camel-swagger-java-starter</artifactId>
        <version>3.15.0</version>
    </dependency>
    <dependency>
        <groupId>org.apache.camel.springboot</groupId>
        <artifactId>camel-spring-boot-starter</artifactId>
        <version>3.15.0</version>
    </dependency>    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

The latest versions of Apache Camel dependencies can be found here.

Apache Camel依赖的最新版本可以在这里找到。

3. The Main Class

3.主课

Let’s first create a Spring Boot Application:

让我们首先创建一个Spring Boot Application

@SpringBootApplication
@ComponentScan(basePackages="com.baeldung.camel")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

4. Camel Configurations for Spring Boot

4.Spring Boot的Camel配置

Let’s now configure our application with Spring, starting with the configuration files (properties).

现在让我们用Spring配置我们的应用程序,从配置文件(属性)开始。

For instance, let’s configure a log for our application on an application.properties file in src/main/resources:

例如,让我们在src/main/resources中的application.properties文件上为我们的应用程序配置一个日志。

logging.config=classpath:logback.xml
camel.springboot.name=MyCamel
server.address=0.0.0.0
management.address=0.0.0.0
management.port=8081
endpoints.enabled = true
endpoints.health.enabled = true

This example shows an application.properties file that also sets the path to a Logback configuration. By setting the IP to “0.0.0.0”, we fully restrict admin and management access on the web server provided by Spring Boot. Also, we enable the needed network access to our application endpoints as well as the health-check endpoints.

这个例子展示了一个application.properties文件,该文件还设置了Logback配置的路径。通过将IP设置为 “0.0.0.0”,我们完全限制了Spring Boot提供的Web服务器上的adminmanagement访问。此外,我们还启用了对我们的应用程序端点以及健康检查端点的必要网络访问。

Another configuration file is the application.yml. In it, we’ll add some properties to helps us inject values into our application routes:

另一个配置文件是application.yml。在该文件中,我们将添加一些属性,以帮助我们向我们的应用程序路由注入值。

server:
  port: 8080
camel:
  springboot:
    name: ServicesRest
management:
  port: 8081
endpoints:
  enabled: false
  health:
    enabled: true
quickstart:
  generateOrderPeriod: 10s
  processOrderPeriod: 30s

5. Setting up the Camel Servlet

5.设置Camel Servlet

One way to start using Camel is to register it as a servlet, so it can intercept the HTTP requests and redirect them to our application.

开始使用Camel的一个方法是把它注册为一个Servlet,这样它就可以拦截HTTP请求并把它们重定向到我们的应用程序。

As mentioned before, staring with Camel’s version 2.18 and below we can take advantage of our application.yml – by creating a parameter for our final URL. Later it will be injected into our Java code:

如前所述,盯着Camel的2.18及以下版本,我们可以利用我们的application.yml – 通过为我们的最终URL创建一个参数。以后它将被注入到我们的Java代码中。

baeldung:
  api:
    path: '/camel'

Back to our Application class, we need to register the Camel servlet at the root of our context path, which is going to be injected from the reference baeldung.api.path in the application.yml when the application starts:

回到我们的Application类,我们需要在上下文路径的根部注册Camel servlet,它将在应用程序启动时从application.yml中的引用baeldung.api.path注入。

@Value("${baeldung.api.path}")
String contextPath;

@Bean
ServletRegistrationBean servletRegistrationBean() {
    ServletRegistrationBean servlet = new ServletRegistrationBean
      (new CamelHttpTransportServlet(), contextPath+"/*");
    servlet.setName("CamelServlet");
    return servlet;
}

As of Camel’s version 2.19, this configuration has been dropped as the CamelServlet is by default set to “/camel”.

从Camel的2.19版本开始,这种配置已经被放弃,因为CamelServlet默认设置为“/camel”

6. Building a Route

6.建立一个路线

Let’s start making a route by extending the RouteBuilder class from Camel, and setting it as a @Component so the component scan routine can locate it during web server initialization:

让我们通过扩展Camel的RouteBuilder类来开始制作一个路由,并将其设置为@Component,这样组件扫描程序就能在Web服务器初始化时找到它。

@Component
class RestApi extends RouteBuilder {
    @Override
    public void configure() {
        CamelContext context = new DefaultCamelContext();
        
        restConfiguration()...
        rest("/api/")... 
        from("direct:remoteService")...
    }
}

In this class, we override the configure() method from Camel’s RouteBuilder class.

在这个类中,我们覆盖了Camel的Configure()类中的RouteBuilder方法。

Camel always needs a CamelContext instance – the core component where the incoming and outgoing messages are kept.

Camel总是需要一个CamelContext实例–这是保存传入和传出信息的核心组件。

In this simple example, DefaultCamelContext suffices as it just binds messages and routes into it, like the REST service that we are going to create.

在这个简单的例子中,DefaultCamelContext就足够了,因为它只是将消息和路由绑定到其中,就像我们将要创建的REST服务。

6.1. The restConfiguration() Route

6.1.restConfiguration() 路线

Next, we create a REST declaration for the endpoints we plan to create in the restConfiguration() method:

接下来,我们在restConfiguration()方法中为我们计划创建的端点创建一个REST声明。

restConfiguration()
  .contextPath(contextPath) 
  .port(serverPort)
  .enableCORS(true)
  .apiContextPath("/api-doc")
  .apiProperty("api.title", "Test REST API")
  .apiProperty("api.version", "v1")
  .apiContextRouteId("doc-api")
  .component("servlet")
  .bindingMode(RestBindingMode.json)

Here, we register the context path with our injected attribute from the YAML file. The same logic was applied to the port of our application. CORS is enabled, allowing for cross-site use of this web service. The binding mode allows and converts arguments to our API.

在这里,我们用YAML文件中注入的属性来注册上下文路径。同样的逻辑也适用于我们应用程序的端口。启用了 CORS,允许跨站使用这个网络服务。绑定模式允许并转换参数到我们的API。

Next, we add Swagger documentation to the URI, title, and version we previously set. As we create methods/endpoints for our REST web service, the Swagger documentation will be automatically updated.

接下来,我们将Swagger文档添加到我们之前设置的URI、标题和版本中。当我们为我们的REST网络服务创建方法/端点时,Swagger文档将被自动更新。

This Swagger context is itself a Camel route, and we can see some technical information about it in the server log during the startup process. Our example documentation is by default served at http://localhost:8080/camel/api-doc.

这个Swagger上下文本身就是一个Camel路由,我们可以在启动过程中的服务器日志中看到关于它的一些技术信息。我们的示例文档默认是在http://localhost:8080/camel/api-doc.提供的。

6.2. The rest() Route

6.2.rest()路线

Now, let’s implement the rest() method call from the configure() method listed above:

现在,让我们从上面列出的configure()方法实现rest()方法调用。

rest("/api/")
  .id("api-route")
  .consumes("application/json")
  .post("/bean")
  .bindingMode(RestBindingMode.json_xml)
  .type(MyBean.class)
  .to("direct:remoteService");

This method is pretty straightforward for those familiar with APIs. The id is the identification of the route inside the CamelContext. The next line defines the MIME type. The binding mode is defined here to show that we can set a mode on the restConfiguration().

对于熟悉API的人来说,这个方法是非常简单明了的。idCamelContext内路由的标识。下一行定义了MIME类型。这里定义了绑定模式,以表明我们可以在restConfiguration()上设置一个模式。

The post() method adds an operation to the API, generating a “POST /bean” endpoint, while the MyBean (a regular Java bean with an Integer id and String name) defines the expected parameters.

post()方法向API添加一个操作,生成一个”POST /bean“端点,而MyBean(一个普通的Java Bean,有Integer idString name)定义了预期参数。

Similarly, HTTP actions such as GET, PUT and DELETE are all available as well in the form of get(), put(), delete().

同样,HTTP操作如GET、PUT和DELETE也都以get()put()delete()的形式提供。

Finally, the to() method creates a bridge to another route. Here it tells Camel to search inside its context/engine to another route that we’re going to create – which is named and detected by the value/id “direct: …“, matching the route defined in the from() method.

最后,to()方法创建了一个通往另一个路由的桥梁。在这里,它告诉Camel在其上下文/引擎中搜索我们将要创建的另一个路由–它被命名并通过值/id”direct: …“检测,与from()方法中定义的路由匹配。

6.3. The from() Route With transform()

6.3.from()路线与transform()

When working with Camel, a route receives parameters and then converts, transforms and process these parameters. After that, it sends these parameters to another route that forwards the result to the desired output (a file, a database, an SMTP server or a REST API response).

当使用Camel时,一个路由接收参数,然后转换、转化和处理这些参数。之后,它将这些参数发送到另一个路由,将结果转发到所需的输出(一个文件、一个数据库、一个SMTP服务器或一个REST API响应)。

In this article, we only create another route inside the configure() method that we are overriding. It will be the destination route for our last to() route:

在本文中,我们只在我们重写的configure()方法中创建了另一个路由。它将是我们最后一个to()路由的目标路由。

from("direct:remoteService")
  .routeId("direct-route")
  .tracing()
  .log(">>> ${body.id}")
  .log(">>> ${body.name}")
  .transform().simple("Hello ${in.body.name}")
  .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200));

The from() method follows the same principles and has many of the same methods as the rest() method, except that it consumes from the Camel context messages. This is the reason for the parameter “direct-route“, that creates a link to the aforementioned method rest().to().

from()方法与rest()方法遵循相同的原则,并有许多相同的方法,只是它从Camel上下文消息中消耗。这就是参数”direct-route“的原因,它创建了一个指向上述方法rest().to()的链接。

Many other conversions are available, including extraction as Java primitives (or objects) and sending it down to a persistence layer. Notice that the routes always read from incoming messages, so that chained routes will ignore outgoing messages.

许多其他的转换是可用的,包括提取为Java原语(或对象)并将其下发到持久层。请注意,路由总是从传入的消息中读取,因此,链式路由将忽略传出的消息。

Our example is ready, and we can try it:

我们的例子已经准备好了,我们可以试试。

  • Run the prompt command: mvn spring-boot:run
  • Do a POST request to http://localhost:8080/camel/api/bean with header parameters: Content-Type: application/json, and a payload {“id”: 1,”name”: “World”}
  • We should receive a return code of 201 and the response: Hello, World

6.4. The SIMPLE Scripting Language

6.4.SIMPLE脚本语言

The example outputs logging using the tracing() method. Notice that we’ve used the ${} placeholders; these are part of a scripting language that belongs to Camel called SIMPLE. It is applied to messages that are exchanged over the route, like the body of the in-message.

这个例子使用tracing()方法输出日志。注意,我们使用了${}占位符;这些是属于Camel的脚本语言的一部分,叫做SIMPLE。它被应用于通过路由交换的消息,如消息中的正文。

In our example, we are using SIMPLE to output to the log the bean attributes that are inside the Camel message body.

在我们的例子中,我们使用SIMPLE来输出Camel消息正文中的bean属性到日志中。

We can also use it to do simple transformations as well, as was shown with the transform() method.

我们也可以用它来做简单的转换,正如用transform()方法所展示的那样。

6.5. The from() Route With process()

6.5.from()路线与process()

Let’s do something more meaningful, such as calling a service layer to return processed data. SIMPLE isn’t meant for heavy data processing, so let’s replace the transform() with a process() method:

让我们做一些更有意义的事情,比如调用一个服务层来返回处理过的数据。SIMPLE并不是为了进行繁重的数据处理,所以让我们用一个process()方法取代transform()

from("direct:remoteService")
  .routeId("direct-route")
  .tracing()
  .log(">>> ${body.id}")
  .log(">>> ${body.name}")
  .process(new Processor() {
      @Override
      public void process(Exchange exchange) throws Exception {
          MyBean bodyIn = (MyBean) exchange.getIn().getBody();
          ExampleServices.example(bodyIn);
          exchange.getIn().setBody(bodyIn);
      }
  })
  .setHeader(Exchange.HTTP_RESPONSE_CODE, constant(200));

This allows us to extract the data into a bean, the same one previously defined on the type() method, and process it in our ExampleServices layer.

这允许我们将数据提取到一个Bean中,也就是之前在type()方法上定义的那个,并在我们的ExampleServices层中处理它。

Since we set the bindingMode() to JSON previously, the response already is in a proper JSON format, generated based on our POJO. This implies that for an ExampleServices class:

由于我们之前将bindingMode()设置为JSON,响应已经是适当的JSON格式,是基于我们的POJO生成的。这意味着对于一个ExampleServices类。

public class ExampleServices {
    public static void example(MyBean bodyIn) {
        bodyIn.setName( "Hello, " + bodyIn.getName() );
        bodyIn.setId(bodyIn.getId() * 10);
    }
}

The same HTTP request now returns with a response code 201 and body: {“id”: 10,”name”: “Hello, World”}.

同样的HTTP请求现在返回一个响应代码201和正文: {“id”:10, “name”:”Hello, World”}

7. Conclusion

7.结论

With a few lines of code, we managed to create a relatively complete application. All dependencies are built, managed and run automatically with a single command. Moreover, we can create APIs that tie together all sorts of technologies.

通过几行代码,我们成功地创建了一个相对完整的应用程序。所有的依赖都是通过一个命令自动建立、管理和运行的。此外,我们可以创建API,将各种技术联系起来。

This approach is also very container friendly, resulting in a very lean server environment that can be easily replicated on demand. The extra configuration possibilities can easily be incorporated into a container template configuration file.

这种方法对容器也非常友好,形成了一个非常精简的服务器环境,可以很容易地按需复制。额外的配置可能性可以很容易地纳入到容器模板配置文件中。

This REST example can be found over on GitHub.

这个REST例子可以在GitHub上找到over

Finally, beyond the filter(), process(), transform(), and marshall() APIs, many other integration patterns and data manipulations are available in Camel:

最后,除了filter()process()transform()marshall() API之外,Camel中还提供了许多其他集成模式和数据操作。